mirror of
https://github.com/alexbosworth/balanceofsatoshis.git
synced 2026-08-13 12:33:37 +02:00
add tag filter to peers list
This commit is contained in:
parent
74bb8db3bb
commit
5e7a51cfe0
13 changed files with 10773 additions and 1121 deletions
|
|
@ -1,5 +1,9 @@
|
|||
# Versions
|
||||
|
||||
## Version 7.19.0
|
||||
|
||||
- `peers`: Add `--tag` option to show only one or more tags in the output
|
||||
|
||||
## Version 7.18.2
|
||||
|
||||
Remove support for auto-detecting TLS IP
|
||||
|
|
|
|||
24
README.md
24
README.md
|
|
@ -249,6 +249,30 @@ expr $(bos balance --node=savedNode1) + $(bos balance --node=savedNode2)
|
|||
# outputs the combined balance of both nodes
|
||||
```
|
||||
|
||||
### Auto Adjust Fees
|
||||
|
||||
```
|
||||
# Cron every 5 minutes adjust fees
|
||||
*/5 * * * * /bin/timeout -s 2 30 /home/ubuntu/update-fees.sh
|
||||
```
|
||||
|
||||
update-fees.sh:
|
||||
|
||||
```
|
||||
#!/bin/bash
|
||||
# Raise the outbound fees to a public key when inbound increases
|
||||
/home/ubuntu/.npm-global/bin/bos fees --to PUBLIC_KEY --set-fee-rate="IF(INBOUND>10000000,1000,500)"
|
||||
```
|
||||
|
||||
### Auto Balance Liquidity Between Two Nodes
|
||||
|
||||
Keep a channel balanced between two nodes
|
||||
|
||||
```
|
||||
# Cron: every 30 minutes send funds to reach 50:50
|
||||
*/30 * * * * /home/ubuntu/.npm-global/bin/bos send PUBKEY --max-fee 0 --message="rebalance" --amount="IF(OUTBOUND+1*m>(LIQUIDITY/2), OUTBOUND-(LIQUIDITY/2), 0)"
|
||||
```
|
||||
|
||||
## Alerts and Reports with `sendnotification`
|
||||
|
||||
Some commands are made with the idea that they can trigger an alert or regular
|
||||
|
|
|
|||
11
bos
11
bos
|
|
@ -607,7 +607,7 @@ prog
|
|||
},
|
||||
responses.returnObject({logger, reject, resolve, table}));
|
||||
} catch (err) {
|
||||
reject(logger.error({err}));
|
||||
return reject(logger.error({err}));
|
||||
}
|
||||
});
|
||||
})
|
||||
|
|
@ -635,7 +635,7 @@ prog
|
|||
},
|
||||
responses.returnObject({logger, reject, resolve}));
|
||||
} catch (err) {
|
||||
return reject(err);
|
||||
return reject(logger.error({err}));
|
||||
}
|
||||
});
|
||||
})
|
||||
|
|
@ -688,7 +688,8 @@ prog
|
|||
|
||||
// Reject inbound channel requests
|
||||
.command('inbound-channel-rules', 'Enforce rules for inbound channels')
|
||||
.help('At least one rule is required')
|
||||
.help('At least one rule is required.')
|
||||
.help('A rule should be written evaluating to TRUE to accept a channel')
|
||||
.help('For formulas: CAPACITIES are the sizes of the peer public channels')
|
||||
.help('For formulas: CAPACITY is the size of the requested channel open')
|
||||
.help('For formulas: CHANNEL_AGES are the block ages of public channels')
|
||||
|
|
@ -756,7 +757,7 @@ prog
|
|||
.option('--max-deposit <max_deposit>', 'Maximum deposit amount', INT, 5e4)
|
||||
.option('--max-fee <max_fee>', 'Maximum estimated fee tokens', INT, 3e4)
|
||||
.option('--max-hours <max_hours>', 'Maximum hours to wait', INT, 65)
|
||||
.option('--max-paths <max_paths>', 'Maximum paths to attempt', INT, 2)
|
||||
.option('--max-paths <max_paths>', 'Maximum paths to attempt', INT, 1)
|
||||
.option('--no-color', 'Mute all colors')
|
||||
.option('--node <node_name>', 'Increase inbound liquidity on saved node')
|
||||
.option('--recovery <recovery>', 'Recover in-progress swap')
|
||||
|
|
@ -1051,6 +1052,7 @@ prog
|
|||
.option('--private', 'Only private channels')
|
||||
.option('--public', 'Only peers with public channels')
|
||||
.option('--sort <by>', 'Sort results by peer attribute', peerSortOptions)
|
||||
.option('--tag <tag_name>', 'Only show peers in a tag', REPEATABLE)
|
||||
.action((args, options, logger) => {
|
||||
const table = !!options.complete ? undefined : 'rows';
|
||||
|
||||
|
|
@ -1071,6 +1073,7 @@ prog
|
|||
omit: flatten([options.omit].filter(n => !!n)),
|
||||
outbound_liquidity_below: options.outboundBelow,
|
||||
sort_by: options.sort,
|
||||
tags: flatten([options.tag].filter(n => !!n)),
|
||||
},
|
||||
responses.returnObject({logger, reject, resolve, table}));
|
||||
} catch (err) {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ const {getTransactionRecord} = require('ln-sync');
|
|||
const {getUtxos} = require('ln-service');
|
||||
const {returnResult} = require('asyncjs-util');
|
||||
|
||||
const describeRelatedChannel = n => `${n.action} with ${n.node} ${n.with}`;
|
||||
const describeChan = n => `${n.action} with ${(n.node || '' + n.with).trim()}`;
|
||||
const flatten = arr => [].concat(...arr);
|
||||
const none = 0;
|
||||
const uniq = arr => Array.from(new Set(arr));
|
||||
|
|
@ -150,7 +150,7 @@ module.exports = (args, cbk) => {
|
|||
.filter(n => n.tx === utxo.transaction_id)
|
||||
.filter(n => n.related_channels.length)
|
||||
.map(n => n.related_channels)
|
||||
.map(chans => chans.map(n => describeRelatedChannel(n)));
|
||||
.map(chans => chans.map(n => describeChan(n)));
|
||||
|
||||
return {
|
||||
outpoint: `${utxo.transaction_id}:${utxo.transaction_vout}`,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ const asyncAuto = require('async/auto');
|
|||
const {returnResult} = require('asyncjs-util');
|
||||
|
||||
const flatten = arr => [].concat(...arr);
|
||||
const {isArray} = Array;
|
||||
const {parse} = JSON;
|
||||
const tagFilePath = () => join(...[homedir(), '.bos', 'tags.json']);
|
||||
const uniq = arr => Array.from(new Set(arr));
|
||||
|
|
@ -20,6 +21,7 @@ const uniq = arr => Array.from(new Set(arr));
|
|||
@returns via cbk or Promise
|
||||
{
|
||||
nodes: [{
|
||||
aliases: [<Alias String>]
|
||||
icons: [<Icon String>]
|
||||
public_key: <Public Key Hex String>
|
||||
}]
|
||||
|
|
@ -51,12 +53,14 @@ module.exports = ({fs}, cbk) => {
|
|||
const keys = uniq(flatten(file.tags.map(n => n.nodes)));
|
||||
|
||||
const nodes = keys.map(key => {
|
||||
const icons = file.tags.filter(tag => {
|
||||
return tag.nodes && tag.nodes.includes(key);
|
||||
// Only tags this node is included in
|
||||
const meta = file.tags.filter(tag => {
|
||||
return isArray(tag.nodes) && tag.nodes.includes(key);
|
||||
});
|
||||
|
||||
return {
|
||||
icons: uniq(icons.map(n => n.icon)),
|
||||
aliases: uniq(meta.map(n => n.alias)),
|
||||
icons: uniq(meta.map(n => n.icon)),
|
||||
public_key: key,
|
||||
};
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
const {Parser} = require('hot-formula-parser');
|
||||
|
||||
const {ceil} = Math;
|
||||
const {isArray} = Array;
|
||||
const {keys} = Object;
|
||||
const {round} = Math;
|
||||
|
||||
|
|
@ -22,6 +23,10 @@ const {round} = Math;
|
|||
}
|
||||
*/
|
||||
module.exports = ({amount, variables}) => {
|
||||
if (isArray(amount)) {
|
||||
throw new Error('CannotParseMultipleAmounts');
|
||||
}
|
||||
|
||||
const parser = new Parser();
|
||||
|
||||
keys(variables || {}).forEach(key => {
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ const tokensAsMillitokens = tok => (BigInt(tok) * BigInt(1e3)).toString();
|
|||
[in_through]: <In Through Public Key Hex String>
|
||||
[is_strict_hints]: <Interpret Routes Strictly Ignoring Other Paths Bool>
|
||||
[is_strict_max_fee]: <Avoid Probing Too-High Fee Routes Bool>
|
||||
lnd: <Authenticated LND gRPC API Object>
|
||||
lnd: <Authenticated LND API Object>
|
||||
logger: <Winston Logger Object>
|
||||
[max_fee]: <Maximum Fee Tokens Number>
|
||||
[max_timeout_height]: <Maximum Timeout Height Number>
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ const wideSizeCols = 150;
|
|||
omit: [<Omit Peer With Public Key Hex String>]
|
||||
[outbound_liquidity_below]: <Outbound Liquidity Below Tokens Number>
|
||||
[sort_by]: <Sort Results By Attribute String>
|
||||
[tags]: [<Tag Identifier String>]
|
||||
}
|
||||
|
||||
@returns via cbk or Promise
|
||||
|
|
@ -97,17 +98,8 @@ module.exports = (args, cbk) => {
|
|||
return cbk();
|
||||
},
|
||||
|
||||
// Get channels
|
||||
getChannels: ['validate', ({}, cbk) => {
|
||||
return getChannels({
|
||||
is_active: args.is_active,
|
||||
is_offline: args.is_offline,
|
||||
is_private: args.is_private,
|
||||
is_public: args.is_public,
|
||||
lnd: args.lnd,
|
||||
},
|
||||
cbk);
|
||||
}],
|
||||
// Get node icons
|
||||
getIcons: ['validate', ({}, cbk) => getIcons({fs: args.fs}, cbk)],
|
||||
|
||||
// Get closed channels
|
||||
getClosed: ['validate', ({}, cbk) => {
|
||||
|
|
@ -128,9 +120,6 @@ module.exports = (args, cbk) => {
|
|||
return getPastForwards({days, lnd: args.lnd}, cbk);
|
||||
}],
|
||||
|
||||
// Get node icons
|
||||
getIcons: ['validate', ({}, cbk) => getIcons({fs: args.fs}, cbk)],
|
||||
|
||||
// Get invoices
|
||||
getInvoices: ['validate', ({}, cbk) => {
|
||||
const invoices = [];
|
||||
|
|
@ -197,6 +186,43 @@ module.exports = (args, cbk) => {
|
|||
return getPendingChannels({lnd: args.lnd}, cbk);
|
||||
}],
|
||||
|
||||
// Get channels
|
||||
getChannels: ['getIcons', ({getIcons}, cbk) => {
|
||||
return getChannels({
|
||||
is_active: args.is_active,
|
||||
is_offline: args.is_offline,
|
||||
is_private: args.is_private,
|
||||
is_public: args.is_public,
|
||||
lnd: args.lnd,
|
||||
},
|
||||
(err, res) => {
|
||||
if (!!err) {
|
||||
return cbk(err);
|
||||
}
|
||||
|
||||
// Filter the list of channels to satisfy tag search constraints
|
||||
const channels = res.channels.filter(channel => {
|
||||
// Exit early when there are no tag constraints
|
||||
if (!isArray(args.tags) || !args.tags.length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const key = channel.partner_public_key;
|
||||
|
||||
const node = getIcons.nodes.find(n => n.public_key === key);
|
||||
|
||||
// Exit early when there is no matching tagged node
|
||||
if (!node) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !!args.tags.find(tag => node.aliases.includes(tag));
|
||||
});
|
||||
|
||||
return cbk(null, {channels});
|
||||
});
|
||||
}],
|
||||
|
||||
// Get policies
|
||||
getPolicies: ['getChannels', ({getChannels}, cbk) => {
|
||||
return asyncMap(getChannels.channels, ({id}, cbk) => {
|
||||
|
|
|
|||
11674
package-lock.json
generated
11674
package-lock.json
generated
File diff suppressed because it is too large
Load diff
24
package.json
24
package.json
|
|
@ -17,34 +17,34 @@
|
|||
"asyncjs-util": "1.2.3",
|
||||
"bitcoin-ops": "1.4.1",
|
||||
"bitcoinjs-lib": "5.2.0",
|
||||
"bolt03": "1.2.6",
|
||||
"bolt07": "1.6.0",
|
||||
"bolt03": "1.2.7",
|
||||
"bolt07": "1.7.0",
|
||||
"caporal": "1.4.0",
|
||||
"cbor": "6.0.1",
|
||||
"cbor": "7.0.1",
|
||||
"cert-info": "1.5.1",
|
||||
"colorette": "1.2.1",
|
||||
"crypto-js": "4.0.0",
|
||||
"csv-parse": "4.15.0",
|
||||
"goldengate": "10.0.0",
|
||||
"csv-parse": "4.15.1",
|
||||
"goldengate": "10.0.1",
|
||||
"hot-formula-parser": "4.0.0",
|
||||
"import-lazy": "4.0.0",
|
||||
"ini": "2.0.0",
|
||||
"inquirer": "7.3.3",
|
||||
"invoices": "1.1.4",
|
||||
"invoices": "1.1.5",
|
||||
"ln-accounting": "4.2.5",
|
||||
"ln-service": "50.11.9",
|
||||
"ln-sync": "0.4.2",
|
||||
"ln-service": "51.1.0",
|
||||
"ln-sync": "0.4.3",
|
||||
"ln-telegram": "3.1.0",
|
||||
"moment": "2.29.1",
|
||||
"node-fetch": "2.6.1",
|
||||
"probing": "1.3.2",
|
||||
"probing": "1.3.3",
|
||||
"psbt": "1.1.7",
|
||||
"qrcode-terminal": "0.12.0",
|
||||
"sanitize-filename": "1.6.3",
|
||||
"stats-lite": "2.2.0",
|
||||
"table": "6.0.7",
|
||||
"telegraf": "4.0.2",
|
||||
"update-notifier": "5.0.1",
|
||||
"telegraf": "4.1.0",
|
||||
"update-notifier": "5.1.0",
|
||||
"window-size": "1.1.1"
|
||||
},
|
||||
"description": "Lightning balance CLI",
|
||||
|
|
@ -76,5 +76,5 @@
|
|||
"postpublish": "docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t alexbosworth/balanceofsatoshis --push .",
|
||||
"test": "tap -t 60 test/arrays/*.js test/balances/*.js test/chain/*.js test/display/*.js test/encryption/*.js test/fiat/*.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/wallets/*.js"
|
||||
},
|
||||
"version": "7.18.2"
|
||||
"version": "7.19.0"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ const filterLimit = 10;
|
|||
const hashOf = n => createHash('sha256').update(n).digest().toString('hex');
|
||||
const hexAsBuffer = hex => Buffer.from(hex, 'hex');
|
||||
const invoiceDescription = n => `👀 ${n}`;
|
||||
const invoiceExpiration = () => new Date(Date.now() + 1000 * 60 * 60 * 24);
|
||||
const invoiceExpiration = () => new Date(Date.now() + 1000 * 60 * 60 * 24 * 5);
|
||||
const invoiceTokens = 1;
|
||||
const keySendValueType = '5482373484';
|
||||
const maxFeeTokens = 10;
|
||||
|
|
|
|||
|
|
@ -656,7 +656,9 @@ module.exports = (args, cbk) => {
|
|||
lnd: args.lnd,
|
||||
logger: args.logger,
|
||||
max_fee: maxExecutionFeeTokens,
|
||||
mtokens: decodeExecutionRequest.mtokens,
|
||||
outgoing_channel: !!channel ? channel.id : undefined,
|
||||
payment: decodeExecutionRequest.payment,
|
||||
routes: decodeExecutionRequest.routes,
|
||||
tokens: decodeExecutionRequest.tokens,
|
||||
},
|
||||
|
|
@ -825,6 +827,7 @@ module.exports = (args, cbk) => {
|
|||
logger: args.logger,
|
||||
max_fee: round(tokens / maxRoutingFeeDenominator),
|
||||
max_paths: args.max_paths || undefined,
|
||||
mtokens: decodeFundingRequest.mtokens,
|
||||
out_through: findPeer.public_key || undefined,
|
||||
outgoing_channel: !!channel ? channel.id : undefined,
|
||||
payment: decodeFundingRequest.payment,
|
||||
|
|
|
|||
|
|
@ -9,48 +9,61 @@ const {pendingChannelsResponse} = require('./../fixtures');
|
|||
|
||||
const getInfoRes = () => JSON.parse(JSON.stringify(getInfoResponse));
|
||||
|
||||
const makeArgs = overrides => {
|
||||
const args = {
|
||||
fs: {getFile: ({}, cbk) => cbk()},
|
||||
lnd: {
|
||||
chain: {
|
||||
registerBlockEpochNtfn: ({}) => {
|
||||
const emitter = new EventEmitter();
|
||||
|
||||
emitter.cancel = () => {};
|
||||
|
||||
process.nextTick(() => emitter.emit('error', 'err'));
|
||||
|
||||
return emitter;
|
||||
},
|
||||
},
|
||||
default: {
|
||||
closedChannels: ({}, cbk) => cbk(null, {channels: []}),
|
||||
getInfo: ({}, cbk) => cbk(null, getInfoRes()),
|
||||
listChannels: ({}, cbk) => cbk(null, {channels: []}),
|
||||
listPeers: ({}, cbk) => cbk(null, {peers: []}),
|
||||
pendingChannels: ({}, cbk) => cbk(null, pendingChannelsResponse),
|
||||
},
|
||||
},
|
||||
omit: [],
|
||||
tags: [],
|
||||
};
|
||||
|
||||
Object.keys(overrides).forEach(k => args[k] = overrides[k]);
|
||||
|
||||
return args;
|
||||
};
|
||||
|
||||
const tests = [
|
||||
{
|
||||
args: {fs: {}},
|
||||
args: makeArgs({lnd: undefined}),
|
||||
description: 'Getting peers requires lnd',
|
||||
error: [400, 'ExpectedLndToGetPeers'],
|
||||
},
|
||||
{
|
||||
args: {fs: {}, lnd: {}},
|
||||
args: makeArgs({omit: undefined}),
|
||||
description: 'Getting peers requires an omit array',
|
||||
error: [400, 'ExpectedOmitArrayToGetPeers'],
|
||||
},
|
||||
{
|
||||
args: {
|
||||
fs: {getFile: ({}, cbk) => cbk()},
|
||||
lnd: {
|
||||
chain: {
|
||||
registerBlockEpochNtfn: ({}) => {
|
||||
const emitter = new EventEmitter();
|
||||
|
||||
emitter.cancel = () => {};
|
||||
|
||||
process.nextTick(() => emitter.emit('error', 'err'));
|
||||
|
||||
return emitter;
|
||||
},
|
||||
},
|
||||
default: {
|
||||
closedChannels: ({}, cbk) => cbk(null, {channels: []}),
|
||||
getInfo: ({}, cbk) => cbk(null, getInfoRes()),
|
||||
listChannels: ({}, cbk) => cbk(null, {channels: []}),
|
||||
listPeers: ({}, cbk) => cbk(null, {peers: []}),
|
||||
pendingChannels: ({}, cbk) => cbk(null, pendingChannelsResponse),
|
||||
},
|
||||
},
|
||||
omit: [],
|
||||
},
|
||||
args: makeArgs({sort_by: []}),
|
||||
description: 'Getting peers requires only one sort factor',
|
||||
error: [400, 'SortingByMultipleFieldsNotSupported'],
|
||||
},
|
||||
{
|
||||
args: makeArgs({}),
|
||||
description: 'Getting peers with no channels returns nothing',
|
||||
expected: {peers: []},
|
||||
},
|
||||
{
|
||||
args: {
|
||||
fs: {getFile: ({}, cbk) => cbk()},
|
||||
args: makeArgs({
|
||||
lnd: {
|
||||
chain: {
|
||||
registerBlockEpochNtfn: ({}) => {
|
||||
|
|
@ -145,8 +158,7 @@ const tests = [
|
|||
listPeers: ({}, cbk) => cbk(null, {peers: []}),
|
||||
},
|
||||
},
|
||||
omit: [],
|
||||
},
|
||||
}),
|
||||
description: 'Getting peers with a channel returns peers',
|
||||
expected: {
|
||||
peers: [{
|
||||
|
|
@ -167,7 +179,7 @@ const tests = [
|
|||
},
|
||||
},
|
||||
{
|
||||
args: {
|
||||
args: makeArgs({
|
||||
fs: {getFile: ({}, cbk) => cbk()},
|
||||
inbound_liquidity_below: 1000,
|
||||
lnd: {
|
||||
|
|
@ -240,9 +252,8 @@ const tests = [
|
|||
pendingChannels: ({}, cbk) => cbk(null, pendingChannelsResponse),
|
||||
},
|
||||
},
|
||||
omit: [],
|
||||
outbound_liquidity_below: 1000,
|
||||
},
|
||||
}),
|
||||
description: 'Getting peers where a channel is missing returns peers',
|
||||
expected: {
|
||||
peers: [{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue