mirror of
https://github.com/alexbosworth/balanceofsatoshis.git
synced 2026-08-13 12:33:37 +02:00
add locking and unlocking of saved node credentials
This commit is contained in:
parent
d7a50cdb51
commit
3abd378bba
21 changed files with 1105 additions and 323 deletions
|
|
@ -1,5 +1,10 @@
|
|||
# Versions
|
||||
|
||||
## Version 4.5.0
|
||||
|
||||
- `nodes`: Add `--lock` and `--unlock` methods to encrypt and decrypt saved node
|
||||
credentials
|
||||
|
||||
## Version 4.4.0
|
||||
|
||||
- `nodes`: Add method to list out all saved nodes
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ bos liquidity-cost "inbound" "amount"
|
|||
# See market price history
|
||||
bos market
|
||||
|
||||
# See list of saved nodes
|
||||
# View and adjust list of saved nodes
|
||||
bos nodes
|
||||
|
||||
# Outputs the sum total of local channel liquidity
|
||||
|
|
|
|||
80
bos
80
bos
|
|
@ -8,6 +8,7 @@ const prog = require('caporal');
|
|||
const {rateProviders} = require('ln-accounting');
|
||||
|
||||
const {accountingCategories} = require('./balances');
|
||||
const {adjustSavedNodes} = require('./nodes');
|
||||
const {authenticatedLnd} = require('./lnd');
|
||||
const {exchanges} = require('./fiat');
|
||||
const {findRecord} = require('./lnd');
|
||||
|
|
@ -21,7 +22,6 @@ const {getLiquidity} = require('./balances');
|
|||
const {getPeers} = require('./network');
|
||||
const {getPriceChart} = require('./fiat');
|
||||
const {getReport} = require('./wallets');
|
||||
const {getSavedNodes} = require('./nodes');
|
||||
const {getSwapCost} = require('./swaps');
|
||||
const {getSwapService} = require('./swaps');
|
||||
const {getUtxos} = require('./chain');
|
||||
|
|
@ -66,16 +66,20 @@ prog
|
|||
const table = !!options.csv ? null : 'rows';
|
||||
|
||||
return new Promise(async (resolve, reject) => {
|
||||
return getAccountingReport({
|
||||
category: args.category,
|
||||
is_csv: !!options.csv,
|
||||
lnd: (await authenticatedLnd({node: options.node})).lnd,
|
||||
month: options.month,
|
||||
node: options.node,
|
||||
rate_provider: options.rateProvider,
|
||||
year: options.year,
|
||||
},
|
||||
returnObject({logger, reject, resolve, table}));
|
||||
try {
|
||||
return getAccountingReport({
|
||||
category: args.category,
|
||||
is_csv: !!options.csv,
|
||||
lnd: (await authenticatedLnd({logger, node: options.node})).lnd,
|
||||
month: options.month,
|
||||
node: options.node,
|
||||
rate_provider: options.rateProvider,
|
||||
year: options.year,
|
||||
},
|
||||
returnObject({logger, reject, resolve, table}));
|
||||
} catch (err) {
|
||||
return reject(err);
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
|
|
@ -295,23 +299,28 @@ prog
|
|||
.option('--show-raw-recovery', 'Show raw recovery transactions')
|
||||
.option('--with <peer>', 'Public key of peer to increase liquidity from')
|
||||
.action((args, options, logger) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
return swapOut({
|
||||
logger,
|
||||
avoid: flatten([options.avoid].filter(n => !!n)),
|
||||
confs: options.confs,
|
||||
is_raw_recovery_shown: options.showRawRecovery || undefined,
|
||||
is_dry_run: options.dryrun || false,
|
||||
max_fee: options.maxFee,
|
||||
max_wait_blocks: Math.ceil((options.maxHours) * 60 / 10),
|
||||
node: options.node || undefined,
|
||||
out_address: options.address || undefined,
|
||||
peer: options.with || undefined,
|
||||
recovery: options.recovery,
|
||||
timeout: 1000 * 60 * 60 * 10,
|
||||
tokens: options.amount,
|
||||
},
|
||||
returnObject({exit, logger, reject, resolve}));
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
return swapOut({
|
||||
logger,
|
||||
avoid: flatten([options.avoid].filter(n => !!n)),
|
||||
confs: options.confs,
|
||||
is_raw_recovery_shown: options.showRawRecovery || undefined,
|
||||
is_dry_run: options.dryrun || false,
|
||||
lnd: (await authenticatedLnd({logger, node: options.node})).lnd,
|
||||
max_fee: options.maxFee,
|
||||
max_wait_blocks: Math.ceil((options.maxHours) * 60 / 10),
|
||||
node: options.node || undefined,
|
||||
out_address: options.address || undefined,
|
||||
peer: options.with || undefined,
|
||||
recovery: options.recovery,
|
||||
timeout: 1000 * 60 * 60 * 10,
|
||||
tokens: options.amount,
|
||||
},
|
||||
returnObject({exit, logger, reject, resolve}));
|
||||
} catch (err) {
|
||||
return reject(err);
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
|
|
@ -354,16 +363,25 @@ prog
|
|||
});
|
||||
})
|
||||
|
||||
// List saved nodes
|
||||
.command('nodes', 'Get the list of saved nodes')
|
||||
// Saved nodes
|
||||
.command('nodes', 'List and edit saved nodes')
|
||||
.help('Locking and unlocking requires existing installation of GPG')
|
||||
.argument('[node]', 'Specify a saved node')
|
||||
.option('--lock <id>', 'Encrypt node authentication to GPG key>', REPEATABLE)
|
||||
.option('--unlock', 'Remove encryption from auth macaroon')
|
||||
.action((args, options, logger) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
return getSavedNodes({
|
||||
return adjustSavedNodes({
|
||||
logger,
|
||||
fs: {
|
||||
writeFile,
|
||||
getDirectoryFiles: readdir,
|
||||
getFile: readFile,
|
||||
getFileStatus: lstat,
|
||||
},
|
||||
is_unlocking: options.unlock || undefined,
|
||||
lock_credentials_to: flatten([options.lock].filter(n => !!n)),
|
||||
node: args.node || undefined,
|
||||
},
|
||||
returnObject({logger, reject, resolve}));
|
||||
});
|
||||
|
|
|
|||
58
encryption/decrypt_ciphertext.js
Normal file
58
encryption/decrypt_ciphertext.js
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
const {spawn} = require('child_process');
|
||||
|
||||
const asyncAuto = require('async/auto');
|
||||
const {returnResult} = require('asyncjs-util');
|
||||
|
||||
/** Decrypt ciphertext that has been encrypted to GPG keys
|
||||
|
||||
{
|
||||
cipher: <Encrypted Text String>
|
||||
}
|
||||
|
||||
@returns via cbk or Promise
|
||||
{
|
||||
clear: <Clear Text String>
|
||||
}
|
||||
*/
|
||||
module.exports = ({cipher}, cbk) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
return asyncAuto({
|
||||
// Check arguments
|
||||
validate: cbk => {
|
||||
if (!cipher) {
|
||||
return cbk([400, 'ExpectedCiphertextToDecrypt']);
|
||||
}
|
||||
|
||||
return cbk();
|
||||
},
|
||||
|
||||
// Decrypt the ciphertext
|
||||
decrypt: ['validate', ({}, cbk) => {
|
||||
const datas = [];
|
||||
const decrypt = spawn('gpg', ['-d']);
|
||||
|
||||
decrypt.stdin.setEncoding('utf-8');
|
||||
|
||||
decrypt.stdout.on('data', data => datas.push(data));
|
||||
decrypt.stdout.on('error', err => cbk([503, 'DecryptionFail', {err}]));
|
||||
|
||||
decrypt.stdout.on('end', () => {
|
||||
if (!datas.length) {
|
||||
return cbk([503, 'FailedToDecrypt']);
|
||||
}
|
||||
|
||||
return cbk(null, {
|
||||
clear: Buffer.concat(datas).toString('utf8').trim(),
|
||||
});
|
||||
});
|
||||
|
||||
decrypt.stdin.write(`${cipher}`);
|
||||
|
||||
decrypt.stdin.end();
|
||||
|
||||
return;
|
||||
}],
|
||||
},
|
||||
returnResult({reject, resolve, of: 'decrypt'}, cbk));
|
||||
});
|
||||
};
|
||||
68
encryption/encrypt_to_public_keys.js
Normal file
68
encryption/encrypt_to_public_keys.js
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
const {spawn} = require('child_process');
|
||||
|
||||
const asyncAuto = require('async/auto');
|
||||
const {returnResult} = require('asyncjs-util');
|
||||
|
||||
const flatten = arr => [].concat(...arr);
|
||||
const {isArray} = Array;
|
||||
|
||||
/** Encrypt a string using a spawned GPG
|
||||
|
||||
{
|
||||
plain: <Plain Clear Text String>
|
||||
to: [<Encrypt To Recipient String>]
|
||||
}
|
||||
|
||||
@returns via cbk or Promise
|
||||
{
|
||||
cipher: <Armored Encrypted Text String>
|
||||
}
|
||||
*/
|
||||
module.exports = ({plain, to}, cbk) => {
|
||||
return new Promise((reject, resolve) => {
|
||||
return asyncAuto({
|
||||
// Check arguments
|
||||
validate: cbk => {
|
||||
if (!plain) {
|
||||
return cbk([400, 'ExpectedPlainTextToEncrypt']);
|
||||
}
|
||||
|
||||
if (!isArray(to) || !to.length) {
|
||||
return cbk([400, 'ExpectedRecipientOfEncryptedData']);
|
||||
}
|
||||
|
||||
return cbk();
|
||||
},
|
||||
|
||||
// Encrypt plain text
|
||||
encrypt: ['validate', ({}, cbk) => {
|
||||
const datas = [];
|
||||
|
||||
const recipients = to
|
||||
.map(n => n.replace(/\s/g, ''))
|
||||
.map(n => (['--recipient', n]));
|
||||
|
||||
const encrypt = spawn('gpg', ['-ea'].concat(flatten(recipients)));
|
||||
|
||||
encrypt.stdin.setEncoding('utf-8');
|
||||
|
||||
encrypt.stdout.on('data', data => datas.push(data));
|
||||
|
||||
encrypt.stdout.on('end', () => {
|
||||
return cbk(null, {
|
||||
cipher: Buffer.concat(datas).toString('utf8').trim(),
|
||||
});
|
||||
});
|
||||
|
||||
encrypt.stdout.on('error', err => cbk([503, 'EncryptingErr', {err}]));
|
||||
|
||||
encrypt.stdin.write(`${plain}\n`);
|
||||
|
||||
encrypt.stdin.end();
|
||||
|
||||
return;
|
||||
}],
|
||||
},
|
||||
returnResult({reject, resolve, of: 'encrypt'}, cbk));
|
||||
});
|
||||
};
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
const decryptCiphertext = require('./decrypt_ciphertext');
|
||||
const decryptPayload = require('./decrypt_payload');
|
||||
const encryptToPublicKeys = require('./encrypt_to_public_keys');
|
||||
|
||||
module.exports = {decryptPayload};
|
||||
module.exports = {decryptCiphertext, decryptPayload, encryptToPublicKeys};
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ const lndCredentials = require('./lnd_credentials');
|
|||
/** Authenticated LND
|
||||
|
||||
{
|
||||
[logger]: <Winston Logger Object>
|
||||
[node]: <Node Name String>
|
||||
}
|
||||
|
||||
|
|
@ -15,11 +16,11 @@ const lndCredentials = require('./lnd_credentials');
|
|||
lnd: <Authenticated LND gRPC API Object>
|
||||
}
|
||||
*/
|
||||
module.exports = ({node}, cbk) => {
|
||||
module.exports = ({logger, node}, cbk) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
return asyncAuto({
|
||||
// Credentials
|
||||
credentials: cbk => lndCredentials({node}, cbk),
|
||||
credentials: cbk => lndCredentials({logger, node}, cbk),
|
||||
|
||||
// Lnd
|
||||
lnd: ['credentials', ({credentials}, cbk) => {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ const asyncDetectSeries = require('async/detectSeries');
|
|||
const {flatten} = require('lodash');
|
||||
const {returnResult} = require('asyncjs-util');
|
||||
|
||||
const {decryptCiphertext} = require('./../encryption');
|
||||
const {getSavedCredentials} = require('./../nodes');
|
||||
const lndDirectory = require('./lnd_directory');
|
||||
|
||||
const base64 = 'base64';
|
||||
|
|
@ -23,6 +25,7 @@ const socket = 'localhost:10009';
|
|||
/** Lnd credentials
|
||||
|
||||
{
|
||||
[logger]: <Winston Logger Object>
|
||||
[node]: <Node Name String> // Defaults to default local mainnet node creds
|
||||
}
|
||||
|
||||
|
|
@ -33,7 +36,7 @@ const socket = 'localhost:10009';
|
|||
socket: <Socket String>
|
||||
}
|
||||
*/
|
||||
module.exports = ({node}, cbk) => {
|
||||
module.exports = ({logger, node}, cbk) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
return asyncAuto({
|
||||
// Get the default cert
|
||||
|
|
@ -100,43 +103,54 @@ module.exports = ({node}, cbk) => {
|
|||
return cbk();
|
||||
}
|
||||
|
||||
const path = [homedir(), home, node, credsFile];
|
||||
|
||||
return readFile(join(...path), (err, creds) => {
|
||||
if (!!err) {
|
||||
return cbk([503, 'FailedToGetNodeCredentials', err]);
|
||||
}
|
||||
|
||||
try {
|
||||
parse(creds);
|
||||
} catch (err) {
|
||||
return cbk([503, 'FailedToParseNodeCredentials', err]);
|
||||
}
|
||||
|
||||
const {cert, macaroon, socket} = parse(creds);
|
||||
|
||||
if (!cert) {
|
||||
return cbk([503, 'FailedToFindCertInCredentials']);
|
||||
}
|
||||
|
||||
if (!macaroon) {
|
||||
return cbk([503, 'FailedToFindMacaroonInCredentials']);
|
||||
}
|
||||
|
||||
if (!socket) {
|
||||
return cbk([503, 'FailedToFindSocketInCredentials']);
|
||||
}
|
||||
|
||||
return cbk(null, {cert, macaroon, socket});
|
||||
});
|
||||
return getSavedCredentials({node, fs: {getFile: readFile}}, cbk);
|
||||
},
|
||||
|
||||
// Node credentials
|
||||
nodeCredentials: ['getNodeCredentials', ({getNodeCredentials}, cbk) => {
|
||||
if (!node) {
|
||||
return cbk();
|
||||
}
|
||||
|
||||
if (!getNodeCredentials.credentials) {
|
||||
return cbk([400, 'CredentialsForSpecifiedNodeNotFound']);
|
||||
}
|
||||
|
||||
const {credentials} = getNodeCredentials;
|
||||
|
||||
if (!credentials.encrypted_macaroon) {
|
||||
return cbk(null, {
|
||||
cert: credentials.cert,
|
||||
macaroon: credentials.macaroon,
|
||||
socket: credentials.socket,
|
||||
});
|
||||
}
|
||||
|
||||
const cipher = credentials.encrypted_macaroon;
|
||||
|
||||
if (!!logger) {
|
||||
logger.info({decrypt_credentials_for: node});
|
||||
}
|
||||
|
||||
return decryptCiphertext({cipher}, (err, res) => {
|
||||
if (!!err) {
|
||||
return cbk(err);
|
||||
}
|
||||
|
||||
return cbk(null, {
|
||||
cert: credentials.cert,
|
||||
macaroon: res.clear,
|
||||
socket: credentials.socket,
|
||||
});
|
||||
});
|
||||
}],
|
||||
|
||||
// Credentials to use
|
||||
credentials: [
|
||||
'getCert',
|
||||
'getMacaroon',
|
||||
'getNodeCredentials',
|
||||
({getCert, getMacaroon, getNodeCredentials}) =>
|
||||
'nodeCredentials',
|
||||
({getCert, getMacaroon, nodeCredentials}) =>
|
||||
{
|
||||
// Exit early with the default credentials when no node is specified
|
||||
if (!node) {
|
||||
|
|
@ -144,9 +158,9 @@ module.exports = ({node}, cbk) => {
|
|||
}
|
||||
|
||||
return cbk(null, {
|
||||
cert: getNodeCredentials.cert,
|
||||
macaroon: getNodeCredentials.macaroon,
|
||||
socket: getNodeCredentials.socket,
|
||||
cert: nodeCredentials.cert,
|
||||
macaroon: nodeCredentials.macaroon,
|
||||
socket: nodeCredentials.socket,
|
||||
});
|
||||
}],
|
||||
},
|
||||
|
|
|
|||
|
|
@ -142,8 +142,6 @@ module.exports = ({node, to, tokens}, cbk) => {
|
|||
if (!!err) {
|
||||
const [errCode, errMessage] = err;
|
||||
|
||||
console.log("ERR", err);
|
||||
|
||||
switch (errMessage) {
|
||||
case 'RejectedUnacceptableFee':
|
||||
return cbk([400, 'GiftTokensAmountTooLowToSend']);
|
||||
|
|
|
|||
131
nodes/adjust_saved_nodes.js
Normal file
131
nodes/adjust_saved_nodes.js
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
const asyncAuto = require('async/auto');
|
||||
const {returnResult} = require('asyncjs-util');
|
||||
|
||||
const decryptSavedMacaroons = require('./decrypt_saved_macaroons');
|
||||
const encryptSavedMacaroons = require('./encrypt_saved_macaroons');
|
||||
const getSavedCredentials = require('./get_saved_credentials');
|
||||
const getSavedNodes = require('./get_saved_nodes');
|
||||
|
||||
const {isArray} = Array;
|
||||
|
||||
/** Adjust or view the set of saved nodes
|
||||
|
||||
{
|
||||
fs: {
|
||||
getDirectoryFiles: <Read Directory Contents Function> (path, cbk) => {}
|
||||
getFile: <Read File Contents Function> (path, cbk) => {}
|
||||
getFileStatus: <File Status Function> (path, cbk) => {}
|
||||
writeFile: <Write File Contents Function> (path, contents, cbk) => {}
|
||||
}
|
||||
[is_unlocking]: <Change Credentials To Decrypted Copy Bool>
|
||||
lock_credentials_to: [<Encrypt Macaroon to GPG Key With Id String>]
|
||||
logger: <Winston Logger Object>
|
||||
[node]: <Node Name String>
|
||||
}
|
||||
|
||||
@returns via cbk or Promise
|
||||
{
|
||||
nodes: [{
|
||||
[is_online]: <Node is Online Bool>
|
||||
[encrypted_to]: [<Encrypted To GPG Id String>]
|
||||
node_name: <Node Name String>
|
||||
}]
|
||||
}
|
||||
*/
|
||||
module.exports = (args, cbk) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
return asyncAuto({
|
||||
// Check arguments
|
||||
validate: cbk => {
|
||||
if (!args.fs) {
|
||||
return cbk([400, 'ExpectedFilesystemMethodsToAdjustSavedNodes']);
|
||||
}
|
||||
|
||||
if (!isArray(args.lock_credentials_to)) {
|
||||
return cbk([400, 'ExpectedArrayOfLockingCredentialGpgIds']);
|
||||
}
|
||||
|
||||
if (!!args.is_unlocking && args.is_unlocking !== true) {
|
||||
return cbk([400, 'UnexpectedArgumentForUnlocking']);
|
||||
}
|
||||
|
||||
if (!!args.is_unlocking && !!args.lock_credentials_to.length) {
|
||||
return cbk([400, 'CannotBothUnlockAndLockNodeCredentials']);
|
||||
}
|
||||
|
||||
if (!args.logger) {
|
||||
return cbk([400, 'ExpectedLoggerFunctionForSavedNodes']);
|
||||
}
|
||||
|
||||
return cbk();
|
||||
},
|
||||
|
||||
// Check specified node exists
|
||||
checkNode: ['validate', ({}, cbk) => {
|
||||
if (!args.node) {
|
||||
return cbk();
|
||||
}
|
||||
|
||||
return getSavedCredentials({
|
||||
fs: args.fs,
|
||||
node: args.node,
|
||||
},
|
||||
(err, res) => {
|
||||
if (!!err) {
|
||||
return cbk(err);
|
||||
}
|
||||
|
||||
if (!res.credentials) {
|
||||
return cbk([404, 'SpecifiedNodeNotFound']);
|
||||
}
|
||||
|
||||
return cbk();
|
||||
});
|
||||
}],
|
||||
|
||||
// Get existing set of nodes
|
||||
getNodes: ['checkNode', ({}, cbk) => getSavedNodes({fs: args.fs}, cbk)],
|
||||
|
||||
// Encrypt macaroons
|
||||
lock: ['getNodes', ({getNodes}, cbk) => {
|
||||
// Exit early when not locking credentials
|
||||
if (!args.lock_credentials_to.length) {
|
||||
return cbk();
|
||||
}
|
||||
|
||||
const {nodes} = getNodes;
|
||||
|
||||
return encryptSavedMacaroons({
|
||||
fs: args.fs,
|
||||
logger: args.logger,
|
||||
nodes: !args.node ? nodes.map(n => n.node_name) : [args.node],
|
||||
to: args.lock_credentials_to,
|
||||
},
|
||||
cbk);
|
||||
}],
|
||||
|
||||
// Unlock credentials
|
||||
unlock: ['getNodes', ({getNodes}, cbk) => {
|
||||
// Exit early when not unlocking credentials
|
||||
if (!args.is_unlocking) {
|
||||
return cbk();
|
||||
}
|
||||
|
||||
const {nodes} = getNodes;
|
||||
|
||||
return decryptSavedMacaroons({
|
||||
fs: args.fs,
|
||||
logger: args.logger,
|
||||
nodes: !args.node ? nodes.map(n => n.node_name) : [args.node],
|
||||
},
|
||||
cbk);
|
||||
}],
|
||||
|
||||
// Get saved nodes
|
||||
getSaved: ['lock', 'unlock', ({}, cbk) => {
|
||||
return getSavedNodes({fs: args.fs}, cbk);
|
||||
}],
|
||||
},
|
||||
returnResult({reject, resolve, of: 'getSaved'}, cbk));
|
||||
});
|
||||
};
|
||||
95
nodes/decrypt_saved_macaroons.js
Normal file
95
nodes/decrypt_saved_macaroons.js
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
const asyncAuto = require('async/auto');
|
||||
const asyncMap = require('async/map');
|
||||
const asyncMapSeries = require('async/mapSeries');
|
||||
const {returnResult} = require('asyncjs-util');
|
||||
|
||||
const {decryptCiphertext} = require('./../encryption');
|
||||
const getSavedCredentials = require('./get_saved_credentials');
|
||||
const putSavedCredentials = require('./put_saved_credentials');
|
||||
|
||||
const {isArray} = Array;
|
||||
|
||||
/** Decrypt saved macaroons and save as cleartext
|
||||
|
||||
{
|
||||
fs: {
|
||||
getFile: <Read File Contents Function> (path, cbk) => {}
|
||||
writeFile: <Write File Contents Function> (path, contents, cbk) => {}
|
||||
}
|
||||
logger: <Winston Logger Object>
|
||||
nodes: [<Node Name String>]
|
||||
}
|
||||
|
||||
@returns via cbk or Promise
|
||||
*/
|
||||
module.exports = ({fs, logger, nodes}, cbk) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
return asyncAuto({
|
||||
// Check arguments
|
||||
validate: cbk => {
|
||||
if (!fs) {
|
||||
return cbk([400, 'ExpectedFileMethodsToDecryptSavedMacaroons']);
|
||||
}
|
||||
|
||||
if (!logger) {
|
||||
return cbk([400, 'ExpectedLoggerToDecryptSavedMacaroons']);
|
||||
}
|
||||
|
||||
if (!isArray(nodes)) {
|
||||
return cbk([400, 'ExpectedNodesToDecryptSavedMacaroons']);
|
||||
}
|
||||
|
||||
return cbk();
|
||||
},
|
||||
|
||||
// Get the credentials
|
||||
getCredentials: ['validate', ({}, cbk) => {
|
||||
return asyncMap(nodes, (node, cbk) => {
|
||||
return getSavedCredentials({fs, node}, cbk);
|
||||
},
|
||||
cbk);
|
||||
}],
|
||||
|
||||
// Decrypt the encrypted macaroons
|
||||
decrypt: ['getCredentials', ({getCredentials}, cbk) => {
|
||||
const encrypted = getCredentials
|
||||
.filter(n => !!n.credentials.encrypted_macaroon);
|
||||
|
||||
return asyncMapSeries(encrypted, ({credentials, node}, cbk) => {
|
||||
const cipher = credentials.encrypted_macaroon;
|
||||
|
||||
logger.info({decrypt_credentials_for: node});
|
||||
|
||||
return decryptCiphertext({cipher}, (err, res) => {
|
||||
if (!!err) {
|
||||
return cbk([503, 'UnexpectedErrorDecryptingMacaroon', {err}]);
|
||||
}
|
||||
|
||||
return cbk(null, {credentials, macaroon: res.clear, node});
|
||||
});
|
||||
},
|
||||
cbk);
|
||||
}],
|
||||
|
||||
// Save the decrypted credentials over the existing credentials
|
||||
save: ['decrypt', ({decrypt}, cbk) => {
|
||||
if (!decrypt.length) {
|
||||
return cbk();
|
||||
}
|
||||
|
||||
return asyncMap(decrypt, ({credentials, macaroon, node}, cbk) => {
|
||||
return putSavedCredentials({
|
||||
fs,
|
||||
macaroon,
|
||||
node,
|
||||
cert: credentials.cert,
|
||||
socket: credentials.socket,
|
||||
},
|
||||
cbk);
|
||||
},
|
||||
cbk);
|
||||
}],
|
||||
},
|
||||
returnResult({reject, resolve}));
|
||||
});
|
||||
};
|
||||
124
nodes/encrypt_saved_macaroons.js
Normal file
124
nodes/encrypt_saved_macaroons.js
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
const asyncAuto = require('async/auto');
|
||||
const asyncMap = require('async/map');
|
||||
const asyncMapSeries = require('async/mapSeries');
|
||||
const {returnResult} = require('asyncjs-util');
|
||||
|
||||
const decryptSavedMacaroons = require('./decrypt_saved_macaroons');
|
||||
const {encryptToPublicKeys} = require('./../encryption');
|
||||
const getSavedCredentials = require('./get_saved_credentials');
|
||||
const putSavedCredentials = require('./put_saved_credentials');
|
||||
|
||||
const ids = n => n.slice().sort().join(',');
|
||||
const {isArray} = Array;
|
||||
const notFoundIndex = -1;
|
||||
|
||||
/** Encrypt saved macaroons to GPG keys
|
||||
|
||||
{
|
||||
fs: {
|
||||
getFile: <Read File Contents Function> (path, cbk) => {}
|
||||
writeFile: <Write File Contents Function> (path, contents, cbk) => {}
|
||||
}
|
||||
logger: <Winston Logger Object>
|
||||
nodes: [<Node Name String>]
|
||||
to: [<Encrypt to GPG Key Id String>]
|
||||
}
|
||||
|
||||
@returns via cbk or Promise
|
||||
*/
|
||||
module.exports = ({fs, logger, nodes, to}, cbk) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
return asyncAuto({
|
||||
// Check arguments
|
||||
validate: cbk => {
|
||||
if (!fs || !fs.getFile || !fs.writeFile) {
|
||||
return cbk([400, 'ExpectedFilesystemMethodsToSaveEncrypted']);
|
||||
}
|
||||
|
||||
if (!isArray(nodes) || !nodes.length) {
|
||||
return cbk([400, 'ExpectedNodesToEncryptSavedMacaroonsFor']);
|
||||
}
|
||||
|
||||
if (!isArray(to) || !to.length) {
|
||||
return cbk([400, 'ExpectedGpgKeyIdsToEncryptSavedMacaroonsTo']);
|
||||
}
|
||||
|
||||
if (to.findIndex(n => typeof(n) !== 'string') !== notFoundIndex) {
|
||||
return cbk([400, 'ExpectedGpgKeyId']);
|
||||
}
|
||||
|
||||
return cbk();
|
||||
},
|
||||
|
||||
// Get the credentials including encrypted credentials
|
||||
getAllCredentials: ['validate', ({}, cbk) => {
|
||||
return asyncMap(nodes, (node, cbk) => {
|
||||
return getSavedCredentials({fs, node}, cbk);
|
||||
},
|
||||
cbk);
|
||||
}],
|
||||
|
||||
// Decrypt macaroons for nodes that have the wrong keys
|
||||
decryptEncrypted: ['getAllCredentials', ({getAllCredentials}, cbk) => {
|
||||
const nodes = getAllCredentials
|
||||
.filter(n => !!n.credentials && !!n.credentials.encrypted_macaroon)
|
||||
.filter(n => ids(n.credentials.encrypted_to) !== ids(to))
|
||||
.map(n => n.node);
|
||||
|
||||
if (!nodes.length) {
|
||||
return cbk();
|
||||
}
|
||||
|
||||
return decryptSavedMacaroons({fs, logger, nodes}, cbk);
|
||||
}],
|
||||
|
||||
// Get the credentials
|
||||
getCredentials: ['decryptEncrypted', ({}, cbk) => {
|
||||
return asyncMap(nodes, (node, cbk) => {
|
||||
return getSavedCredentials({fs, node}, cbk);
|
||||
},
|
||||
cbk);
|
||||
}],
|
||||
|
||||
// Encrypt unencrypted macaroons
|
||||
encrypt: ['getCredentials', ({getCredentials}, cbk) => {
|
||||
const plainCredentials = getCredentials
|
||||
.filter(n => !!n.credentials && !!n.credentials.macaroon);
|
||||
|
||||
return asyncMapSeries(plainCredentials, ({credentials, node}, cbk) => {
|
||||
const plain = credentials.macaroon;
|
||||
|
||||
return encryptToPublicKeys({plain, to}, (err, res) => {
|
||||
if (!!err) {
|
||||
return cbk([503, 'UnexpectedErrorEncryptingMacaroon', {err}]);
|
||||
}
|
||||
|
||||
return cbk(null, {credentials, node, cipher: res.cipher});
|
||||
});
|
||||
},
|
||||
cbk);
|
||||
}],
|
||||
|
||||
// Save the encrypted credentials over the existing credentials
|
||||
save: ['encrypt', ({encrypt}, cbk) => {
|
||||
if (!encrypt.length) {
|
||||
return cbk();
|
||||
}
|
||||
|
||||
return asyncMap(encrypt, ({credentials, node, cipher}, cbk) => {
|
||||
return putSavedCredentials({
|
||||
fs,
|
||||
node,
|
||||
cert: credentials.cert,
|
||||
encrypted_macaroon: cipher,
|
||||
encrypted_to: to,
|
||||
socket: credentials.socket,
|
||||
},
|
||||
cbk);
|
||||
},
|
||||
cbk);
|
||||
}],
|
||||
},
|
||||
returnResult({reject, resolve}, cbk));
|
||||
});
|
||||
};
|
||||
97
nodes/get_saved_credentials.js
Normal file
97
nodes/get_saved_credentials.js
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
const {join} = require('path');
|
||||
const {homedir} = require('os');
|
||||
|
||||
const asyncAuto = require('async/auto');
|
||||
const {returnResult} = require('asyncjs-util');
|
||||
|
||||
const credentials = 'credentials.json';
|
||||
const home = '.bos';
|
||||
const {parse} = JSON;
|
||||
|
||||
/** Get saved credentials for node
|
||||
|
||||
{
|
||||
fs: {
|
||||
getFile: <Read File Contents Function> (path, cbk) => {}
|
||||
}
|
||||
node: <Node Name String>
|
||||
}
|
||||
|
||||
@returns via cbk or Promise
|
||||
{
|
||||
[credentials]: {
|
||||
cert: <Base64 or Hex Serialized LND TLS Cert>
|
||||
[encrypted_macaroon]: <Encrypted Macaroon String>
|
||||
[encrypted_to]: [<Encrypted to GPG Recipient String>]
|
||||
[macaroon]: <Base64 or Hex Serialized Macaroon String>
|
||||
socket: <Host:Port Network Address String>
|
||||
}
|
||||
node: <Node Name String>
|
||||
}
|
||||
*/
|
||||
module.exports = ({fs, node}, cbk) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
return asyncAuto({
|
||||
// Check arguments
|
||||
validate: cbk => {
|
||||
if (!fs || !fs.getFile) {
|
||||
return cbk([400, 'ExpectedFileGetMethodToGetSavedCredentials']);
|
||||
}
|
||||
|
||||
if (!node) {
|
||||
return cbk([400, 'ExpectedNodeNameToGetSavedCredentials']);
|
||||
}
|
||||
|
||||
return cbk();
|
||||
},
|
||||
|
||||
// Get credentials
|
||||
getCredentials: ['validate', ({}, cbk) => {
|
||||
const path = join(...[homedir(), home, node, credentials]);
|
||||
|
||||
return fs.getFile(path, (err, res) => {
|
||||
// Exit early on errors, there is no credential found
|
||||
if (!!err || !res) {
|
||||
return cbk(null, {node});
|
||||
}
|
||||
|
||||
try {
|
||||
parse(res.toString());
|
||||
} catch (err) {
|
||||
return cbk([400, 'SavedNodeHasInvalidCredentials']);
|
||||
}
|
||||
|
||||
const credentials = parse(res.toString());
|
||||
|
||||
if (!credentials.cert) {
|
||||
return cbk([400, 'SavedNodeMissingCertData']);
|
||||
}
|
||||
|
||||
if (!credentials.macaroon && !credentials.encrypted_macaroon) {
|
||||
return cbk([400, 'SavedNodeMissingMacaroonData']);
|
||||
}
|
||||
|
||||
if (!!credentials.encrypted_macaroon && !credentials.encrypted_to) {
|
||||
return cbk([400, 'MissingEncryptToRecipientsInSavedCredentials']);
|
||||
}
|
||||
|
||||
if (!credentials.socket) {
|
||||
return cbk([400, 'SavedNodeMissingSocket']);
|
||||
}
|
||||
|
||||
return cbk(null, {
|
||||
node,
|
||||
credentials: {
|
||||
cert: credentials.cert,
|
||||
encrypted_macaroon: credentials.encrypted_macaroon,
|
||||
encrypted_to: credentials.encrypted_to,
|
||||
macaroon: credentials.macaroon,
|
||||
socket: credentials.socket,
|
||||
},
|
||||
});
|
||||
});
|
||||
}],
|
||||
},
|
||||
returnResult({reject, resolve, of: 'getCredentials'}, cbk));
|
||||
});
|
||||
};
|
||||
|
|
@ -8,17 +8,17 @@ const {authenticatedLndGrpc} = require('ln-service');
|
|||
const {getWalletInfo} = require('ln-service');
|
||||
const {returnResult} = require('asyncjs-util');
|
||||
|
||||
const credentials = 'credentials.json';
|
||||
const getSavedCredentials = require('./get_saved_credentials');
|
||||
|
||||
const home = '.bos';
|
||||
const {parse} = JSON;
|
||||
|
||||
/** Get a list of saved nodes
|
||||
|
||||
{
|
||||
fs: {
|
||||
getDirectoryFiles: <Read Directory Contents Function> (path, cbk) => {}
|
||||
getFile: <Read File Contents Function> (path, cbk) => {}
|
||||
getFileStatus: <File Status Function> (path, cbk) => {}
|
||||
getDirectoryFiles: <Read Directory Contents Function> (path, cbk) => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -42,6 +42,10 @@ module.exports = ({fs}, cbk) => {
|
|||
return cbk([400, 'ExpectedFileSystemMethods']);
|
||||
}
|
||||
|
||||
if (!fs.getDirectoryFiles) {
|
||||
return cbk([400, 'ExpectedGetDirectoryFilesMethod']);
|
||||
}
|
||||
|
||||
if (!fs.getFile) {
|
||||
return cbk([400, 'ExpectedReadFileFunction']);
|
||||
}
|
||||
|
|
@ -50,10 +54,6 @@ module.exports = ({fs}, cbk) => {
|
|||
return cbk([400, 'ExpectedReadFileStatusFunction']);
|
||||
}
|
||||
|
||||
if (!fs.getDirectoryFiles) {
|
||||
return cbk([400, 'ExpectedGetDirectoryFilesMethod']);
|
||||
}
|
||||
|
||||
return cbk();
|
||||
},
|
||||
|
||||
|
|
@ -61,7 +61,7 @@ module.exports = ({fs}, cbk) => {
|
|||
checkDataDir: ['dataDir', ({dataDir}, cbk) => {
|
||||
return fs.getFileStatus(dataDir, (err, res) => {
|
||||
if (!!err) {
|
||||
return cbk([503, 'UnexpectedErrorCheckingForDataDirectory', {err}]);
|
||||
return cbk([503, 'UnexpectedErrCheckingForDataDirectory', {err}]);
|
||||
}
|
||||
|
||||
if (!res.isDirectory()) {
|
||||
|
|
@ -92,54 +92,31 @@ module.exports = ({fs}, cbk) => {
|
|||
|
||||
// Get node credentials
|
||||
getNodeCredentials: ['getDirs', ({getDirs}, cbk) => {
|
||||
const credentialPaths = getDirs.map(dir => {
|
||||
return {dir, path: join(...[homedir(), home, dir, credentials])};
|
||||
});
|
||||
|
||||
return asyncMap(credentialPaths, ({dir, path}, cbk) => {
|
||||
return fs.getFile(path, (err, res) => {
|
||||
if (!!err || !res) {
|
||||
return cbk();
|
||||
}
|
||||
|
||||
try {
|
||||
parse(res.toString());
|
||||
} catch (err) {
|
||||
return cbk([400, 'SavedNodeHasInvalidCredentials', {err, path}]);
|
||||
}
|
||||
|
||||
const credentials = parse(res.toString());
|
||||
|
||||
if (!credentials.cert) {
|
||||
return cbk([400, 'SavedNodeMissingCertData', {dir}]);
|
||||
}
|
||||
|
||||
if (!credentials.macaroon) {
|
||||
return cbk([400, 'SavedNodeMissingCertData', {dir}]);
|
||||
}
|
||||
|
||||
if (!credentials.socket) {
|
||||
return cbk([400, 'SavedNodeMissingSocket', {dir}]);
|
||||
}
|
||||
|
||||
return cbk(null, {credentials, dir});
|
||||
});
|
||||
return asyncMap(getDirs, (node, cbk) => {
|
||||
return getSavedCredentials({fs, node}, cbk);
|
||||
},
|
||||
cbk);
|
||||
}],
|
||||
|
||||
// Get node info
|
||||
getNodes: ['getNodeCredentials', ({getNodeCredentials}, cbk) => {
|
||||
return asyncMap(getNodeCredentials, ({credentials, dir}, cbk) => {
|
||||
return asyncMap(getNodeCredentials, ({credentials, node}, cbk) => {
|
||||
if (!credentials.macaroon) {
|
||||
return cbk(null, {
|
||||
node_name: node,
|
||||
locked_to_keys: credentials.encrypted_to,
|
||||
});
|
||||
}
|
||||
|
||||
const {lnd} = authenticatedLndGrpc(credentials);
|
||||
|
||||
return getWalletInfo({lnd}, (err, res) => {
|
||||
if (!!err) {
|
||||
return cbk(null, {node_name: dir});
|
||||
return cbk(null, {node_name: node});
|
||||
}
|
||||
|
||||
return cbk(null, {
|
||||
node_name: dir,
|
||||
node_name: node,
|
||||
is_online: res.is_synced_to_chain,
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
const adjustSavedNodes = require('./adjust_saved_nodes');
|
||||
const getSavedCredentials = require('./get_saved_credentials');
|
||||
const getSavedNodes = require('./get_saved_nodes');
|
||||
|
||||
module.exports = {getSavedNodes};
|
||||
module.exports = {adjustSavedNodes, getSavedCredentials, getSavedNodes};
|
||||
|
|
|
|||
87
nodes/put_saved_credentials.js
Normal file
87
nodes/put_saved_credentials.js
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
const {join} = require('path');
|
||||
const {homedir} = require('os');
|
||||
|
||||
const asyncAuto = require('async/auto');
|
||||
const {returnResult} = require('asyncjs-util');
|
||||
|
||||
const credentials = 'credentials.json';
|
||||
const home = '.bos';
|
||||
const {isArray} = Array;
|
||||
const stringify = obj => JSON.stringify(obj, null, 2);
|
||||
|
||||
/** Write saved credentials for node
|
||||
|
||||
{
|
||||
cert: <Base64 Encoded Node TLS Certificate String>
|
||||
[encrypted_macaroon]: <Encrypted Macaroon String>
|
||||
[encrypted_to]: [<Macaroon Encrypted To Recipient Id String>]
|
||||
fs: {
|
||||
writeFile: <Write File Contents Function> (path, contents, cbk) => {}
|
||||
}
|
||||
[macaroon]: <Base64 Encoded Macaroon String>
|
||||
node: <Node Name String>
|
||||
socket: <Node Socket String>
|
||||
}
|
||||
|
||||
@returns via cbk or Promise
|
||||
*/
|
||||
module.exports = (args, cbk) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
return asyncAuto({
|
||||
// Check arguments
|
||||
validate: cbk => {
|
||||
if (!args.cert) {
|
||||
return cbk([400, 'ExpectedCertToWriteSavedCredentialsForNode']);
|
||||
}
|
||||
|
||||
if (!!args.encrypted_macaroon && !isArray(args.encrypted_to)) {
|
||||
return cbk([400, 'ExpectedRecipientIdsForEncryptedMacaroon']);
|
||||
}
|
||||
|
||||
if (!!args.encrypted_macaroon && !!args.macaroon) {
|
||||
return cbk([400, 'UnexpectedUnencryptedMacaroon']);
|
||||
}
|
||||
|
||||
if (!args.encrypted_macaroon && !args.macaroon) {
|
||||
return cbk([400, 'ExpectedMacaroonForSavedCredentials']);
|
||||
}
|
||||
|
||||
if (!args.fs || !args.fs.writeFile) {
|
||||
return cbk([400, 'ExpectedFileSystemMethodsToPutSavedCredentials']);
|
||||
}
|
||||
|
||||
if (!args.node) {
|
||||
return cbk([400, 'ExpectedNodeNameToPutSavedCredentials']);
|
||||
}
|
||||
|
||||
if (!args.socket) {
|
||||
return cbk([400, 'ExpectedSocketForNodeToPutSavedCredentials']);
|
||||
}
|
||||
|
||||
return cbk();
|
||||
},
|
||||
|
||||
// Write credentials
|
||||
writeCredentials: ['validate', ({}, cbk) => {
|
||||
const file = stringify({
|
||||
cert: args.cert,
|
||||
encrypted_macaroon: args.encrypted_macaroon || undefined,
|
||||
encrypted_to: args.encrypted_to || undefined,
|
||||
macaroon: args.macaroon || undefined,
|
||||
socket: args.socket,
|
||||
});
|
||||
|
||||
const path = join(...[homedir(), home, args.node, credentials]);
|
||||
|
||||
return args.fs.writeFile(path, file, err => {
|
||||
if (!!err) {
|
||||
return cbk([503, 'UnexpectedErrorWritingSavedCredentials']);
|
||||
}
|
||||
|
||||
return cbk();
|
||||
});
|
||||
}],
|
||||
},
|
||||
returnResult({reject, resolve, of: 'getCredentials'}, cbk));
|
||||
});
|
||||
};
|
||||
262
package-lock.json
generated
262
package-lock.json
generated
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "balanceofsatoshis",
|
||||
"version": "4.4.0",
|
||||
"version": "4.5.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
|
@ -440,9 +440,9 @@
|
|||
"integrity": "sha512-4vx/aaY6j/j3Lw3fbCHNWP0pPaTCew3F6F3hYyl/tHs/ndmV1q7NW9T5yuJ2XAGwdQrP+6Wu20x06U4APo/iQQ=="
|
||||
},
|
||||
"async-hook-domain": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/async-hook-domain/-/async-hook-domain-1.1.1.tgz",
|
||||
"integrity": "sha512-nHfgkoCbzXqCPEFshW5/LROfUqKcZdOW4mfvR66V7bdSuvvvt+s2CuHVBmJxHfOVFhCJ29xlRqEJxyNQKQsqBg==",
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/async-hook-domain/-/async-hook-domain-1.1.3.tgz",
|
||||
"integrity": "sha512-ZovMxSbADV3+biB7oR1GL5lGyptI24alp0LWHlmz1OFc5oL47pz3EiIF6nXOkDW7yLqih4NtsiYduzdDW0i+Wg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"source-map-support": "^0.5.11"
|
||||
|
|
@ -951,12 +951,6 @@
|
|||
"winston": "^2.3.1"
|
||||
}
|
||||
},
|
||||
"capture-stack-trace": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz",
|
||||
"integrity": "sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw==",
|
||||
"dev": true
|
||||
},
|
||||
"caseless": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
|
||||
|
|
@ -1001,19 +995,19 @@
|
|||
}
|
||||
},
|
||||
"chokidar": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.2.1.tgz",
|
||||
"integrity": "sha512-/j5PPkb5Feyps9e+jo07jUZGvkB5Aj953NrI4s8xSVScrAo/RHeILrtdb4uzR7N6aaFFxxJ+gt8mA8HfNpw76w==",
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.2.2.tgz",
|
||||
"integrity": "sha512-bw3pm7kZ2Wa6+jQWYP/c7bAZy3i4GwiIiMO2EeRjrE48l8vBqC/WvFhSF0xyM8fQiPEGvwMY/5bqDG7sSEOuhg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"anymatch": "~3.1.1",
|
||||
"braces": "~3.0.2",
|
||||
"fsevents": "~2.1.0",
|
||||
"fsevents": "~2.1.1",
|
||||
"glob-parent": "~5.1.0",
|
||||
"is-binary-path": "~2.1.0",
|
||||
"is-glob": "~4.0.1",
|
||||
"normalize-path": "~3.0.0",
|
||||
"readdirp": "~3.1.3"
|
||||
"readdirp": "~3.2.0"
|
||||
}
|
||||
},
|
||||
"ci-info": {
|
||||
|
|
@ -5092,9 +5086,9 @@
|
|||
}
|
||||
},
|
||||
"readdirp": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.1.3.tgz",
|
||||
"integrity": "sha512-ZOsfTGkjO2kqeR5Mzr5RYDbTGYneSkdNKX2fOX2P5jF7vMrd/GNnIAUtDldeHHumHUCQ3V05YfWUdxMPAsRu9Q==",
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz",
|
||||
"integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"picomatch": "^2.0.4"
|
||||
|
|
@ -5419,8 +5413,7 @@
|
|||
},
|
||||
"source-map-support": {
|
||||
"version": "0.5.13",
|
||||
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz",
|
||||
"integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==",
|
||||
"resolved": "github:tapjs/node-source-map-support#b2e96dd5c02f70a62f06ca2eea7f344bf9f825c8",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"buffer-from": "^1.0.0",
|
||||
|
|
@ -5633,15 +5626,14 @@
|
|||
}
|
||||
},
|
||||
"tap": {
|
||||
"version": "14.6.9",
|
||||
"resolved": "https://registry.npmjs.org/tap/-/tap-14.6.9.tgz",
|
||||
"integrity": "sha512-impxvxJo49knWdQNctdY+hAyQGbLjGR9foNkX4B9NnotgV5tfmJNqLrFu+YluHSNhIjfqF6Ea+Pj08xPAHxfSQ==",
|
||||
"version": "14.7.1",
|
||||
"resolved": "https://registry.npmjs.org/tap/-/tap-14.7.1.tgz",
|
||||
"integrity": "sha512-hbX34cNSduEh0RMNMIUwZPwJksBnE+LQC8FfvvOeZGvkUTXVVxZ4/ADuuDP2AY+9e75t8ZqzezAsfzIQHSLNSg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"async-hook-domain": "^1.1.1",
|
||||
"async-hook-domain": "^1.1.2",
|
||||
"bind-obj-methods": "^2.0.0",
|
||||
"browser-process-hrtime": "^1.0.0",
|
||||
"capture-stack-trace": "^1.0.0",
|
||||
"chokidar": "^3.0.2",
|
||||
"color-support": "^1.1.0",
|
||||
"coveralls": "^3.0.6",
|
||||
|
|
@ -5667,7 +5659,7 @@
|
|||
"react": "^16.9.0",
|
||||
"rimraf": "^2.7.1",
|
||||
"signal-exit": "^3.0.0",
|
||||
"source-map-support": "^0.5.13",
|
||||
"source-map-support": "github:tapjs/node-source-map-support#node-header-length-change",
|
||||
"stack-utils": "^1.0.2",
|
||||
"tap-mocha-reporter": "^5.0.0",
|
||||
"tap-parser": "^10.0.0",
|
||||
|
|
@ -5677,14 +5669,14 @@
|
|||
"trivial-deferred": "^1.0.1",
|
||||
"ts-node": "^8.3.0",
|
||||
"typescript": "^3.6.3",
|
||||
"which": "^1.3.1",
|
||||
"which": "^2.0.1",
|
||||
"write-file-atomic": "^3.0.0",
|
||||
"yaml": "^1.6.0",
|
||||
"yapool": "^1.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": {
|
||||
"version": "7.4.5",
|
||||
"version": "7.6.3",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
|
|
@ -5692,19 +5684,19 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"regenerator-runtime": {
|
||||
"version": "0.13.2",
|
||||
"version": "0.13.3",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"@types/prop-types": {
|
||||
"version": "15.7.1",
|
||||
"version": "15.7.3",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
},
|
||||
"@types/react": {
|
||||
"version": "16.8.22",
|
||||
"version": "16.9.5",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
|
|
@ -5713,9 +5705,12 @@
|
|||
}
|
||||
},
|
||||
"ansi-escapes": {
|
||||
"version": "3.2.0",
|
||||
"version": "4.2.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"type-fest": "^0.5.2"
|
||||
}
|
||||
},
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
|
|
@ -5743,7 +5738,7 @@
|
|||
"dev": true
|
||||
},
|
||||
"auto-bind": {
|
||||
"version": "2.1.0",
|
||||
"version": "2.1.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
|
|
@ -6063,15 +6058,22 @@
|
|||
"dev": true,
|
||||
"requires": {
|
||||
"safe-buffer": "~5.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"core-js": {
|
||||
"version": "2.6.5",
|
||||
"version": "2.6.10",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
},
|
||||
"csstype": {
|
||||
"version": "2.6.5",
|
||||
"version": "2.6.7",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
},
|
||||
|
|
@ -6107,7 +6109,7 @@
|
|||
"dev": true
|
||||
},
|
||||
"esutils": {
|
||||
"version": "2.0.2",
|
||||
"version": "2.0.3",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
},
|
||||
|
|
@ -6157,11 +6159,12 @@
|
|||
}
|
||||
},
|
||||
"ink": {
|
||||
"version": "2.3.0",
|
||||
"version": "2.5.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/react": "^16.8.6",
|
||||
"ansi-escapes": "^4.2.1",
|
||||
"arrify": "^1.0.1",
|
||||
"auto-bind": "^2.0.0",
|
||||
"chalk": "^2.4.1",
|
||||
|
|
@ -6171,8 +6174,8 @@
|
|||
"lodash.throttle": "^4.1.1",
|
||||
"log-update": "^3.0.0",
|
||||
"prop-types": "^15.6.2",
|
||||
"react-reconciler": "^0.20.0",
|
||||
"scheduler": "^0.13.2",
|
||||
"react-reconciler": "^0.21.0",
|
||||
"scheduler": "^0.15.0",
|
||||
"signal-exit": "^3.0.2",
|
||||
"slice-ansi": "^1.0.0",
|
||||
"string-length": "^2.0.0",
|
||||
|
|
@ -6181,11 +6184,6 @@
|
|||
"yoga-layout-prebuilt": "^1.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-regex": {
|
||||
"version": "4.1.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "3.2.1",
|
||||
"bundled": true,
|
||||
|
|
@ -6204,24 +6202,6 @@
|
|||
"supports-color": "^5.3.0"
|
||||
}
|
||||
},
|
||||
"string-width": {
|
||||
"version": "3.1.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"emoji-regex": "^7.0.1",
|
||||
"is-fullwidth-code-point": "^2.0.0",
|
||||
"strip-ansi": "^5.1.0"
|
||||
}
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "5.2.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "5.5.0",
|
||||
"bundled": true,
|
||||
|
|
@ -6229,16 +6209,6 @@
|
|||
"requires": {
|
||||
"has-flag": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"wrap-ansi": {
|
||||
"version": "5.1.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^3.2.0",
|
||||
"string-width": "^3.0.0",
|
||||
"strip-ansi": "^5.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -6287,7 +6257,7 @@
|
|||
"dev": true
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.14",
|
||||
"version": "4.17.15",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
},
|
||||
|
|
@ -6297,7 +6267,7 @@
|
|||
"dev": true
|
||||
},
|
||||
"log-update": {
|
||||
"version": "3.2.0",
|
||||
"version": "3.3.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
|
|
@ -6306,46 +6276,10 @@
|
|||
"wrap-ansi": "^5.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-regex": {
|
||||
"version": "4.1.0",
|
||||
"ansi-escapes": {
|
||||
"version": "3.2.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "3.2.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-convert": "^1.9.0"
|
||||
}
|
||||
},
|
||||
"string-width": {
|
||||
"version": "3.1.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"emoji-regex": "^7.0.1",
|
||||
"is-fullwidth-code-point": "^2.0.0",
|
||||
"strip-ansi": "^5.1.0"
|
||||
}
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "5.2.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"wrap-ansi": {
|
||||
"version": "5.1.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^3.2.0",
|
||||
"string-width": "^3.0.0",
|
||||
"strip-ansi": "^5.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -6357,6 +6291,11 @@
|
|||
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||
}
|
||||
},
|
||||
"mimic-fn": {
|
||||
"version": "1.2.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.0.4",
|
||||
"bundled": true,
|
||||
|
|
@ -6366,7 +6305,7 @@
|
|||
}
|
||||
},
|
||||
"minipass": {
|
||||
"version": "3.0.0",
|
||||
"version": "3.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
|
|
@ -6416,13 +6355,6 @@
|
|||
"dev": true,
|
||||
"requires": {
|
||||
"mimic-fn": "^1.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"mimic-fn": {
|
||||
"version": "1.2.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"os-homedir": {
|
||||
|
|
@ -6461,7 +6393,7 @@
|
|||
"dev": true
|
||||
},
|
||||
"react": {
|
||||
"version": "16.9.0",
|
||||
"version": "16.10.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
|
|
@ -6471,19 +6403,19 @@
|
|||
}
|
||||
},
|
||||
"react-is": {
|
||||
"version": "16.8.6",
|
||||
"version": "16.10.2",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
},
|
||||
"react-reconciler": {
|
||||
"version": "0.20.4",
|
||||
"version": "0.21.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.1.1",
|
||||
"prop-types": "^15.6.2",
|
||||
"scheduler": "^0.13.6"
|
||||
"scheduler": "^0.15.0"
|
||||
}
|
||||
},
|
||||
"redeyed": {
|
||||
|
|
@ -6521,13 +6453,8 @@
|
|||
"signal-exit": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
},
|
||||
"scheduler": {
|
||||
"version": "0.13.6",
|
||||
"version": "0.15.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
|
|
@ -6700,6 +6627,20 @@
|
|||
"bundled": true,
|
||||
"dev": true
|
||||
},
|
||||
"type-fest": {
|
||||
"version": "0.5.2",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
},
|
||||
"which": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.1.tgz",
|
||||
"integrity": "sha512-N7GBZOTswtB9lkQBZA4+zAXrjEIWAUOB93AvzUiudRzRxhUdLURQ7D/gAIMY1gatT/LTbmbcv8SiYazy3eYB7w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"isexe": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"widest-line": {
|
||||
"version": "2.0.1",
|
||||
"bundled": true,
|
||||
|
|
@ -6708,10 +6649,53 @@
|
|||
"string-width": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"wrap-ansi": {
|
||||
"version": "5.1.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^3.2.0",
|
||||
"string-width": "^3.0.0",
|
||||
"strip-ansi": "^5.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-regex": {
|
||||
"version": "4.1.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "3.2.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-convert": "^1.9.0"
|
||||
}
|
||||
},
|
||||
"string-width": {
|
||||
"version": "3.1.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"emoji-regex": "^7.0.1",
|
||||
"is-fullwidth-code-point": "^2.0.0",
|
||||
"strip-ansi": "^5.1.0"
|
||||
}
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "5.2.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^4.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"write-file-atomic": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.0.tgz",
|
||||
"integrity": "sha512-EIgkf60l2oWsffja2Sf2AL384dx328c0B+cIYPTQq5q2rOYuDV00/iPFBOUiDKKwKMOhkymH8AidPaRvzfxY+Q==",
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.1.tgz",
|
||||
"integrity": "sha512-JPStrIyyVJ6oCSz/691fAjFtefZ6q+fP6tm+OS4Qw6o+TGQxNp1ziY2PgS+X/m0V8OWhZiO/m4xSj+Pr4RrZvw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"imurmurhash": "^0.1.4",
|
||||
|
|
@ -6721,11 +6705,11 @@
|
|||
}
|
||||
},
|
||||
"yaml": {
|
||||
"version": "1.6.0",
|
||||
"version": "1.7.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.4.5"
|
||||
"@babel/runtime": "^7.5.5"
|
||||
}
|
||||
},
|
||||
"yoga-layout-prebuilt": {
|
||||
|
|
@ -6944,9 +6928,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"uglify-js": {
|
||||
"version": "3.6.1",
|
||||
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.1.tgz",
|
||||
"integrity": "sha512-+dSJLJpXBb6oMHP+Yvw8hUgElz4gLTh82XuX68QiJVTXaE5ibl6buzhNkQdYhBlIhozWOC9ge16wyRmjG4TwVQ==",
|
||||
"version": "3.6.2",
|
||||
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.2.tgz",
|
||||
"integrity": "sha512-+gh/xFte41GPrgSMJ/oJVq15zYmqr74pY9VoM69UzMzq9NFk4YDylclb1/bhEzZSaUQjbW5RvniHeq1cdtRYjw==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@
|
|||
},
|
||||
"description": "Lightning balance CLI",
|
||||
"devDependencies": {
|
||||
"tap": "14.6.9"
|
||||
"tap": "14.7.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.4.0"
|
||||
|
|
@ -47,7 +47,7 @@
|
|||
"url": "https://github.com/alexbosworth/balanceofsatoshis.git"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "tap test/arrays/*.js test/balances/*.js test/encryption/*.js test/fiat/*.js test/network/*.js test/responses/*.js test/routing/*.js"
|
||||
"test": "tap test/arrays/*.js test/balances/*.js test/encryption/*.js test/fiat/*.js test/network/*.js test/nodes/*.js test/responses/*.js test/routing/*.js"
|
||||
},
|
||||
"version": "4.4.0"
|
||||
"version": "4.5.0"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ module.exports = (args, cbk) => {
|
|||
|
||||
// Get authenticated lnd connection
|
||||
getLnd: ['validate', ({}, cbk) => {
|
||||
return authenticatedLnd({node: args.node}, cbk);
|
||||
return authenticatedLnd({logger: args.logger, node: args.node}, cbk);
|
||||
}],
|
||||
|
||||
// Get wallet info
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ const tokensAsBigUnit = tokens => (tokens / 1e8).toFixed(8);
|
|||
confs: <Confirmations to Wait for Deposit Number>
|
||||
[is_dry_run]: <Avoid Actually Executing Operation Bool>
|
||||
[is_raw_recovery_shown]: <Show Raw Recovery Transactions Bool>
|
||||
lnd: <Authenticated LND gRPC API Object>
|
||||
logger: <Winston Logger Object>
|
||||
[max_fee]: <Maximum Fee Tokens Number>
|
||||
[max_wait_blocks]: <Maximum Wait Blocks Number>
|
||||
|
|
@ -103,6 +104,10 @@ module.exports = (args, cbk) => {
|
|||
return cbk([400, 'ExpectedConfirmationsCountToConsiderReorgSafe']);
|
||||
}
|
||||
|
||||
if (!args.lnd) {
|
||||
return cbk([400, 'ExpectedLndToInitiateSwapOut']);
|
||||
}
|
||||
|
||||
if (!args.logger) {
|
||||
return cbk([400, 'ExpectedLoggerForSwapProgressNotifications']);
|
||||
}
|
||||
|
|
@ -118,13 +123,8 @@ module.exports = (args, cbk) => {
|
|||
return cbk();
|
||||
}],
|
||||
|
||||
// Get authenticated lnd connection
|
||||
getLnd: ['validate', ({}, cbk) => {
|
||||
return authenticatedLnd({node: args.node}, cbk);
|
||||
}],
|
||||
|
||||
// Create a sweep address
|
||||
createAddress: ['getLnd', 'recover', ({getLnd, recover}, cbk) => {
|
||||
createAddress: ['recover', 'validate', ({recover}, cbk) => {
|
||||
if (!!recover && recover.sweep_address) {
|
||||
return cbk(null, {address: recover.sweep_address});
|
||||
}
|
||||
|
|
@ -136,24 +136,22 @@ module.exports = (args, cbk) => {
|
|||
return createChainAddress({
|
||||
format: 'p2wpkh',
|
||||
is_unused: true,
|
||||
lnd: getLnd.lnd,
|
||||
lnd: args.lnd,
|
||||
},
|
||||
cbk);
|
||||
}],
|
||||
|
||||
// Get channels
|
||||
getChannels: ['getLnd', ({getLnd}, cbk) => {
|
||||
return getChannels({lnd: getLnd.lnd, is_active: true}, cbk);
|
||||
getChannels: ['validate', ({}, cbk) => {
|
||||
return getChannels({lnd: args.lnd, is_active: true}, cbk);
|
||||
}],
|
||||
|
||||
// Get network
|
||||
getNetwork: ['getLnd', ({getLnd}, cbk) => {
|
||||
return getNetwork({lnd: getLnd.lnd}, cbk);
|
||||
}],
|
||||
getNetwork: ['validate', ({}, cbk) => getNetwork({lnd: args.lnd}, cbk)],
|
||||
|
||||
// Get wallet info
|
||||
getWalletInfo: ['getLnd', ({getLnd}, cbk) => {
|
||||
return getWalletInfo({lnd: getLnd.lnd}, cbk);
|
||||
getWalletInfo: ['validate', ({}, cbk) => {
|
||||
return getWalletInfo({lnd: args.lnd}, cbk);
|
||||
}],
|
||||
|
||||
// Get the current block height
|
||||
|
|
@ -162,7 +160,7 @@ module.exports = (args, cbk) => {
|
|||
}],
|
||||
|
||||
// Figure out which channel to use when swapping
|
||||
channel: ['getChannels', 'getLnd', ({getChannels, getLnd}, cbk) => {
|
||||
channel: ['getChannels', ({getChannels}, cbk) => {
|
||||
if (!!args.recovery) {
|
||||
return cbk();
|
||||
}
|
||||
|
|
@ -194,7 +192,7 @@ module.exports = (args, cbk) => {
|
|||
|
||||
return getNode({
|
||||
is_omitting_channels: true,
|
||||
lnd: getLnd.lnd,
|
||||
lnd: args.lnd,
|
||||
public_key: channel.partner_public_key,
|
||||
},
|
||||
(err, res) => {
|
||||
|
|
@ -339,17 +337,13 @@ module.exports = (args, cbk) => {
|
|||
}],
|
||||
|
||||
// Decode swap execution request
|
||||
decodeExecutionRequest: [
|
||||
'getLnd',
|
||||
'initiateSwap',
|
||||
({getLnd, initiateSwap}, cbk) =>
|
||||
{
|
||||
decodeExecutionRequest: ['initiateSwap', ({initiateSwap}, cbk) => {
|
||||
if (!!args.recovery) {
|
||||
return cbk();
|
||||
}
|
||||
|
||||
return decodePaymentRequest({
|
||||
lnd: getLnd.lnd,
|
||||
lnd: args.lnd,
|
||||
request: initiateSwap.swap_execute_request,
|
||||
},
|
||||
cbk);
|
||||
|
|
@ -409,17 +403,13 @@ module.exports = (args, cbk) => {
|
|||
}],
|
||||
|
||||
// Decode funding request
|
||||
decodeFundingRequest: [
|
||||
'getLnd',
|
||||
'initiateSwap',
|
||||
({getLnd, initiateSwap}, cbk) =>
|
||||
{
|
||||
decodeFundingRequest: ['initiateSwap', ({initiateSwap}, cbk) => {
|
||||
if (!!args.recovery) {
|
||||
return cbk();
|
||||
}
|
||||
|
||||
return decodePaymentRequest({
|
||||
lnd: getLnd.lnd,
|
||||
lnd: args.lnd,
|
||||
request: initiateSwap.swap_fund_request,
|
||||
},
|
||||
cbk);
|
||||
|
|
@ -457,9 +447,8 @@ module.exports = (args, cbk) => {
|
|||
findRouteForExecution: [
|
||||
'channel',
|
||||
'decodeExecutionRequest',
|
||||
'getLnd',
|
||||
'getStartHeight',
|
||||
({channel, decodeExecutionRequest, getLnd, getStartHeight}, cbk) =>
|
||||
({channel, decodeExecutionRequest, getStartHeight}, cbk) =>
|
||||
{
|
||||
// Exit early when there is a swap recovery
|
||||
if (!!args.recovery) {
|
||||
|
|
@ -470,7 +459,7 @@ module.exports = (args, cbk) => {
|
|||
cltv_delta: decodeExecutionRequest.cltv_delta + cltvBuffer,
|
||||
destination: decodeExecutionRequest.destination,
|
||||
ignore: (args.avoid || []).map(n => ({from_public_key: n})),
|
||||
lnd: getLnd.lnd,
|
||||
lnd: args.lnd,
|
||||
logger: args.logger,
|
||||
max_fee: maxExecutionFeeTokens,
|
||||
max_timeout_height: getStartHeight + maxCltvDelta,
|
||||
|
|
@ -496,13 +485,11 @@ module.exports = (args, cbk) => {
|
|||
'channel',
|
||||
'currency',
|
||||
'decodeFundingRequest',
|
||||
'getLnd',
|
||||
'getStartHeight',
|
||||
({
|
||||
channel,
|
||||
currency,
|
||||
decodeFundingRequest,
|
||||
getLnd,
|
||||
getStartHeight,
|
||||
}, cbk) =>
|
||||
{
|
||||
|
|
@ -514,7 +501,7 @@ module.exports = (args, cbk) => {
|
|||
cltv_delta: decodeFundingRequest.cltv_delta + cltvBuffer,
|
||||
destination: decodeFundingRequest.destination,
|
||||
ignore: (args.avoid || []).map(n => ({from_public_key: n})),
|
||||
lnd: getLnd.lnd,
|
||||
lnd: args.lnd,
|
||||
logger: args.logger,
|
||||
max_fee: round(decodeFundingRequest.tokens / maxRoutingFeeDenominator),
|
||||
max_timeout_height: getStartHeight + maxCltvDelta,
|
||||
|
|
@ -540,8 +527,7 @@ module.exports = (args, cbk) => {
|
|||
'channel',
|
||||
'findRouteForFunding',
|
||||
'getChannels',
|
||||
'getLnd',
|
||||
({channel, findRouteForFunding, getChannels, getLnd}, cbk) =>
|
||||
({channel, findRouteForFunding, getChannels}, cbk) =>
|
||||
{
|
||||
// Exit early when this is a recovery
|
||||
if (!!args.recovery) {
|
||||
|
|
@ -563,7 +549,7 @@ module.exports = (args, cbk) => {
|
|||
|
||||
return getNode({
|
||||
is_omitting_channels: true,
|
||||
lnd: getLnd.lnd,
|
||||
lnd: args.lnd,
|
||||
public_key: firstHop.public_key,
|
||||
},
|
||||
(err, res) => {
|
||||
|
|
@ -588,7 +574,6 @@ module.exports = (args, cbk) => {
|
|||
'decodeFundingRequest',
|
||||
'findRouteForExecution',
|
||||
'findRouteForFunding',
|
||||
'getLnd',
|
||||
'getSwapPeer',
|
||||
'getQuote',
|
||||
({
|
||||
|
|
@ -597,7 +582,6 @@ module.exports = (args, cbk) => {
|
|||
decodeFundingRequest,
|
||||
findRouteForExecution,
|
||||
findRouteForFunding,
|
||||
getLnd,
|
||||
getSwapPeer,
|
||||
getQuote,
|
||||
}, cbk) =>
|
||||
|
|
@ -622,7 +606,7 @@ module.exports = (args, cbk) => {
|
|||
|
||||
return getChainFeeRate({
|
||||
confirmation_target: getQuote.cltv_delta,
|
||||
lnd: getLnd.lnd,
|
||||
lnd: args.lnd,
|
||||
},
|
||||
(err, res) => {
|
||||
if (!!err) {
|
||||
|
|
@ -661,8 +645,7 @@ module.exports = (args, cbk) => {
|
|||
'decodeFundingRequest',
|
||||
'findRouteForExecution',
|
||||
'findRouteForFunding',
|
||||
'getLnd',
|
||||
({decodeFundingRequest, findRouteForFunding, getLnd}, cbk) =>
|
||||
({decodeFundingRequest, findRouteForFunding}, cbk) =>
|
||||
{
|
||||
if (!!args.recovery) {
|
||||
return cbk();
|
||||
|
|
@ -670,7 +653,7 @@ module.exports = (args, cbk) => {
|
|||
|
||||
return payViaRoutes({
|
||||
id: decodeFundingRequest.id,
|
||||
lnd: getLnd.lnd,
|
||||
lnd: args.lnd,
|
||||
routes: [findRouteForFunding],
|
||||
},
|
||||
cbk);
|
||||
|
|
@ -684,7 +667,6 @@ module.exports = (args, cbk) => {
|
|||
'decodeExecutionRequest',
|
||||
'findRouteForExecution',
|
||||
'findRouteForFunding',
|
||||
'getLnd',
|
||||
'getMinSweepFee',
|
||||
'getQuote',
|
||||
'getStartHeight',
|
||||
|
|
@ -692,7 +674,6 @@ module.exports = (args, cbk) => {
|
|||
({
|
||||
channel,
|
||||
decodeExecutionRequest,
|
||||
getLnd,
|
||||
getQuote,
|
||||
getStartHeight,
|
||||
initiateSwap,
|
||||
|
|
@ -705,7 +686,7 @@ module.exports = (args, cbk) => {
|
|||
args.logger.info({paying_execution_request: decodeExecutionRequest.id});
|
||||
|
||||
const sub = subscribeToPayViaRequest({
|
||||
lnd: getLnd.lnd,
|
||||
lnd: args.lnd,
|
||||
max_fee: maxExecutionFeeTokens,
|
||||
max_timeout_height: getStartHeight + maxCltvExpiration,
|
||||
outgoing_channel: channel.id || undefined,
|
||||
|
|
@ -774,15 +755,14 @@ module.exports = (args, cbk) => {
|
|||
|
||||
// Look for deposit
|
||||
findDeposit: [
|
||||
'getLnd',
|
||||
'getWalletInfo',
|
||||
'initiateSwap',
|
||||
'network',
|
||||
'recover',
|
||||
({getWalletInfo, initiateSwap, getLnd, network, recover}, cbk) =>
|
||||
({getWalletInfo, initiateSwap, network, recover}, cbk) =>
|
||||
{
|
||||
const currentHeight = getWalletInfo.current_block_height;
|
||||
const sub = subscribeToBlocks({lnd: getLnd.lnd});
|
||||
const sub = subscribeToBlocks({lnd: args.lnd});
|
||||
const tokens = !recover ? args.tokens : recover.tokens;
|
||||
|
||||
const startHeight = !recover ? currentHeight : recover.start_height;
|
||||
|
|
@ -803,7 +783,7 @@ module.exports = (args, cbk) => {
|
|||
address: initiateSwap.address,
|
||||
after: startHeight - fuzzBlocks,
|
||||
confirmations: args.confs,
|
||||
lnd: getLnd.lnd,
|
||||
lnd: args.lnd,
|
||||
timeout: args.timeout,
|
||||
},
|
||||
(err, res) => {
|
||||
|
|
@ -847,7 +827,6 @@ module.exports = (args, cbk) => {
|
|||
'claim',
|
||||
'createAddress',
|
||||
'getHeight',
|
||||
'getLnd',
|
||||
'initiateSwap',
|
||||
'network',
|
||||
'recover',
|
||||
|
|
@ -855,7 +834,6 @@ module.exports = (args, cbk) => {
|
|||
claim,
|
||||
createAddress,
|
||||
getHeight,
|
||||
getLnd,
|
||||
initiateSwap,
|
||||
network,
|
||||
recover,
|
||||
|
|
@ -881,7 +859,7 @@ module.exports = (args, cbk) => {
|
|||
current_height: getHeight + i,
|
||||
deadline_height: min(maxWaitHeight, maxSafeHeight),
|
||||
is_dry_run: true,
|
||||
lnd: getLnd.lnd,
|
||||
lnd: args.lnd,
|
||||
max_fee_multiplier: maxFeeMultiplier,
|
||||
min_fee_rate: minFeeRate,
|
||||
private_key: claim.private_key,
|
||||
|
|
@ -917,7 +895,6 @@ module.exports = (args, cbk) => {
|
|||
'claim',
|
||||
'createAddress',
|
||||
'getHeight',
|
||||
'getLnd',
|
||||
'initiateSwap',
|
||||
'network',
|
||||
'rawRecovery',
|
||||
|
|
@ -926,7 +903,6 @@ module.exports = (args, cbk) => {
|
|||
claim,
|
||||
createAddress,
|
||||
getHeight,
|
||||
getLnd,
|
||||
initiateSwap,
|
||||
network,
|
||||
recover,
|
||||
|
|
@ -940,7 +916,7 @@ module.exports = (args, cbk) => {
|
|||
|
||||
args.logger.info({swap_deposit_confirmed: claim.transaction_id});
|
||||
|
||||
const blocksSubscription = subscribeToBlocks({lnd: getLnd.lnd});
|
||||
const blocksSubscription = subscribeToBlocks({lnd: args.lnd});
|
||||
const tokens = !recover ? args.tokens : recover.tokens;
|
||||
|
||||
blocksSubscription.on('end', () => {});
|
||||
|
|
@ -955,7 +931,7 @@ module.exports = (args, cbk) => {
|
|||
tokens,
|
||||
current_height: height,
|
||||
deadline_height: initiateSwap.timeout - args.confs,
|
||||
lnd: getLnd.lnd,
|
||||
lnd: args.lnd,
|
||||
max_fee_multiplier: maxFeeMultiplier,
|
||||
private_key: claim.private_key,
|
||||
secret: claim.secret,
|
||||
|
|
@ -993,7 +969,7 @@ module.exports = (args, cbk) => {
|
|||
address: createAddress.address,
|
||||
after: getHeight,
|
||||
confirmations: max(args.confs, minSweepConfs),
|
||||
lnd: getLnd.lnd,
|
||||
lnd: args.lnd,
|
||||
timeout: args.timeout,
|
||||
transaction_id: claim.transaction_id,
|
||||
transaction_vout: claim.transaction_vout,
|
||||
|
|
@ -1012,17 +988,16 @@ module.exports = (args, cbk) => {
|
|||
// Get funding payment
|
||||
getFundingPayment: [
|
||||
'decodeFundingRequest',
|
||||
'getLnd',
|
||||
'payToFund',
|
||||
'recover',
|
||||
'sweep',
|
||||
({decodeFundingRequest, getLnd, recover}, cbk) =>
|
||||
({decodeFundingRequest, recover}, cbk) =>
|
||||
{
|
||||
const fundingRequest = decodeFundingRequest || {};
|
||||
|
||||
const id = fundingRequest.id || sha256(recover.secret).digest('hex');
|
||||
|
||||
const sub = subscribeToPastPayment({id, lnd: getLnd.lnd});
|
||||
const sub = subscribeToPastPayment({id, lnd: args.lnd});
|
||||
|
||||
const finished = (err, res) => {
|
||||
sub.removeAllListeners();
|
||||
|
|
@ -1046,16 +1021,15 @@ module.exports = (args, cbk) => {
|
|||
// Get execution payment
|
||||
getExecutionPayment: [
|
||||
'decodeExecutionRequest',
|
||||
'getLnd',
|
||||
'payToExecute',
|
||||
'recover',
|
||||
({decodeExecutionRequest, getLnd, recover}, cbk) =>
|
||||
({decodeExecutionRequest, recover}, cbk) =>
|
||||
{
|
||||
const executionRequest = decodeExecutionRequest || {};
|
||||
|
||||
const id = executionRequest.id || recover.execution_id;
|
||||
|
||||
return getPayment({id, lnd: getLnd.lnd}, cbk);
|
||||
return getPayment({id, lnd: args.lnd}, cbk);
|
||||
}],
|
||||
|
||||
// Spent offchain
|
||||
|
|
|
|||
147
test/nodes/test_get_saved_nodes.js
Normal file
147
test/nodes/test_get_saved_nodes.js
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
const {test} = require('tap');
|
||||
|
||||
const {getSavedNodes} = require('./../../nodes');
|
||||
|
||||
const getDirectoryFiles = ({}, cbk) => cbk(null, ['name']);
|
||||
|
||||
const tests = [
|
||||
{
|
||||
args: {},
|
||||
description: 'Getting saved nodes requires fs',
|
||||
error: [400, 'ExpectedFileSystemMethods'],
|
||||
},
|
||||
{
|
||||
args: {fs: {}},
|
||||
description: 'Filesystem methods requires a directory files function',
|
||||
error: [400, 'ExpectedGetDirectoryFilesMethod'],
|
||||
},
|
||||
{
|
||||
args: {fs: {getDirectoryFiles: () => {}}},
|
||||
description: 'Filesystem methods requires a get file function',
|
||||
error: [400, 'ExpectedReadFileFunction'],
|
||||
},
|
||||
{
|
||||
args: {fs: {getDirectoryFiles: ({}, cbk) => {}, getFile: ({}, cbk) => {}}},
|
||||
description: 'Filesystem methods requires a read file function',
|
||||
error: [400, 'ExpectedReadFileStatusFunction'],
|
||||
},
|
||||
{
|
||||
args: {
|
||||
fs: {
|
||||
getDirectoryFiles: ({}, cbk) => {},
|
||||
getFile: ({}, cbk) => {},
|
||||
getFileStatus: ({}, cbk) => cbk('err'),
|
||||
},
|
||||
},
|
||||
description: 'Error getting data dir returns back error',
|
||||
error: [503, 'UnexpectedErrCheckingForDataDirectory', {err: 'err'}],
|
||||
},
|
||||
{
|
||||
args: {
|
||||
fs: {
|
||||
getDirectoryFiles: ({}, cbk) => {},
|
||||
getFile: ({}, cbk) => {},
|
||||
getFileStatus: ({}, cbk) => cbk(null, {isDirectory: () => false}),
|
||||
},
|
||||
},
|
||||
description: 'The home directory must be a directory',
|
||||
error: [400, 'FailedToFindHomeDataDirectory'],
|
||||
},
|
||||
{
|
||||
args: {
|
||||
fs: {
|
||||
getDirectoryFiles: ({}, cbk) => cbk(null, []),
|
||||
getFile: ({}, cbk) => cbk(),
|
||||
getFileStatus: ({}, cbk) => cbk(null, {isDirectory: () => true}),
|
||||
},
|
||||
},
|
||||
description: 'A directory with no saved nodes returns an empty array',
|
||||
expected: {nodes: []},
|
||||
},
|
||||
{
|
||||
args: {
|
||||
fs: {
|
||||
getDirectoryFiles,
|
||||
getFile: ({}, cbk) => cbk(),
|
||||
getFileStatus: (path, cbk) => {
|
||||
if (path.slice(-'name'.length) === 'name') {
|
||||
return cbk('err');
|
||||
}
|
||||
|
||||
return cbk(null, {isDirectory: () => true});
|
||||
},
|
||||
},
|
||||
},
|
||||
description: 'Errors when getting node dir are passed back',
|
||||
error: [503, 'UnexpectedErrCheckingForNodeDir', {err: 'err'}],
|
||||
},
|
||||
{
|
||||
args: {
|
||||
fs: {
|
||||
getDirectoryFiles,
|
||||
getFile: ({}, cbk) => cbk(null, 'foo'),
|
||||
getFileStatus: (path, cbk) => {
|
||||
return cbk(null, {isDirectory: () => true});
|
||||
},
|
||||
},
|
||||
},
|
||||
description: 'The saved node has to have JSON credentials',
|
||||
error: [400, 'SavedNodeHasInvalidCredentials'],
|
||||
},
|
||||
{
|
||||
args: {
|
||||
fs: {
|
||||
getDirectoryFiles,
|
||||
getFile: ({}, cbk) => cbk(null, JSON.stringify({})),
|
||||
getFileStatus: (path, cbk) => {
|
||||
return cbk(null, {isDirectory: () => true});
|
||||
},
|
||||
},
|
||||
},
|
||||
description: 'The saved node has to have JSON credentials',
|
||||
error: [400, 'SavedNodeMissingCertData'],
|
||||
},
|
||||
{
|
||||
args: {
|
||||
fs: {
|
||||
getDirectoryFiles,
|
||||
getFile: ({}, cbk) => cbk(null, JSON.stringify({cert: 'cert'})),
|
||||
getFileStatus: (path, cbk) => {
|
||||
return cbk(null, {isDirectory: () => true});
|
||||
},
|
||||
},
|
||||
},
|
||||
description: 'The saved node has to have JSON credentials',
|
||||
error: [400, 'SavedNodeMissingMacaroonData'],
|
||||
},
|
||||
{
|
||||
args: {
|
||||
fs: {
|
||||
getDirectoryFiles,
|
||||
getFile: ({}, cbk) => cbk(null, JSON.stringify({
|
||||
cert: 'cert',
|
||||
macaroon: 'macaroon',
|
||||
})),
|
||||
getFileStatus: (path, cbk) => {
|
||||
return cbk(null, {isDirectory: () => true});
|
||||
},
|
||||
},
|
||||
},
|
||||
description: 'The saved node has to have JSON credentials',
|
||||
error: [400, 'SavedNodeMissingSocket'],
|
||||
},
|
||||
];
|
||||
|
||||
tests.forEach(({args, description, error, expected}) => {
|
||||
return test(description, async ({deepIs, end, equal, rejects}) => {
|
||||
if (!!error) {
|
||||
rejects(getSavedNodes(args), error, 'Got expected error');
|
||||
} else {
|
||||
const {nodes} = await getSavedNodes(args);
|
||||
|
||||
deepIs(nodes, expected.nodes, 'Got expected nodes');
|
||||
}
|
||||
|
||||
return end();
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue