mirror of
https://github.com/alexbosworth/balanceofsatoshis.git
synced 2026-08-13 12:33:37 +02:00
remove hot-formula-parser dependency
This commit is contained in:
parent
4239ba093d
commit
3eb9ff7ff3
14 changed files with 662 additions and 432 deletions
|
|
@ -1,5 +1,12 @@
|
|||
# Versions
|
||||
|
||||
## 23.0.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
- Remove support for extended range of formulas, supported formulas are here:
|
||||
https://github.com/alexbosworth/formulas#custom-functions
|
||||
|
||||
## 22.1.7
|
||||
|
||||
- Add support for LND 0.21.1
|
||||
|
|
|
|||
|
|
@ -564,8 +564,11 @@ alias bosd="docker run -d --rm -v $HOME/.bos:/home/node/.bos alexbosworth/balanc
|
|||
Some commands take formula arguments. Formulas are expressions that allow you
|
||||
to perform functions and reference variables.
|
||||
|
||||
There is a dynamic playground here where you can play with expressions:
|
||||
https://formulajs.info/functions/
|
||||
Formulas are modeled after common spreadsheet formulas, but there is not a 1:1
|
||||
mapping of supported functions from all spreadsheets.
|
||||
|
||||
See supported formula construction here:
|
||||
https://github.com/alexbosworth/formulas#custom-functions
|
||||
|
||||
### `--avoid`
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +0,0 @@
|
|||
const {parseErrors} = require('./constants');
|
||||
|
||||
const defaultError = '#ERROR!';
|
||||
|
||||
/** Describe a hot formula parser error
|
||||
|
||||
{
|
||||
error: <Error Type String>
|
||||
}
|
||||
|
||||
@returns
|
||||
<Display String>
|
||||
*/
|
||||
module.exports = ({error}) => {
|
||||
return parseErrors[error] || parseErrors[defaultError];
|
||||
};
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
const chartAliasForPeer = require('./chart_alias_for_peer');
|
||||
const describeConfidence = require('./describe_confidence');
|
||||
const describeParseError = require('./describe_parse_error');
|
||||
const describeRoute = require('./describe_route');
|
||||
const describeRoutingFailure = require('./describe_routing_failure');
|
||||
const formatFeeRate = require('./format_fee_rate');
|
||||
|
|
@ -12,7 +11,6 @@ const sumsForSegment = require('./sums_for_segment');
|
|||
module.exports = {
|
||||
chartAliasForPeer,
|
||||
describeConfidence,
|
||||
describeParseError,
|
||||
describeRoute,
|
||||
describeRoutingFailure,
|
||||
formatFeeRate,
|
||||
|
|
|
|||
|
|
@ -1,10 +1,7 @@
|
|||
const {Parser} = require('hot-formula-parser');
|
||||
|
||||
const describeParseError = require('./describe_parse_error');
|
||||
const {evaluateFormula} = require('@alexbosworth/formulas');
|
||||
|
||||
const {assign} = Object;
|
||||
const defaultVariables = {btc: 1e8, k: 1e3, m: 1e6, mm: 1e6};
|
||||
const {keys} = Object;
|
||||
|
||||
/** Determine if variables are consistent with filters
|
||||
|
||||
|
|
@ -32,25 +29,14 @@ module.exports = ({filters, variables}) => {
|
|||
|
||||
const vars = {};
|
||||
|
||||
[defaultVariables, variables].forEach(n => assign(vars, n));
|
||||
[defaultVariables, variables].forEach(variable => assign(vars, variable));
|
||||
|
||||
const filtered = filters.map(formula => {
|
||||
const parser = new Parser();
|
||||
|
||||
keys(vars).forEach(key => {
|
||||
parser.setVariable(key.toLowerCase(), vars[key]);
|
||||
parser.setVariable(key.toUpperCase(), vars[key]);
|
||||
|
||||
return;
|
||||
});
|
||||
|
||||
const parsed = parser.parse(formula);
|
||||
|
||||
if (!!parsed.error) {
|
||||
return {formula, error: describeParseError({error: parsed.error})};
|
||||
try {
|
||||
return !evaluateFormula({constants: vars, formula}).result;
|
||||
} catch (err) {
|
||||
return {formula, error: err.message};
|
||||
}
|
||||
|
||||
return parsed.result === false;
|
||||
});
|
||||
|
||||
const [errored] = filtered.filter(n => !!n.error);
|
||||
|
|
|
|||
781
package-lock.json
generated
781
package-lock.json
generated
File diff suppressed because it is too large
Load diff
18
package.json
18
package.json
|
|
@ -13,6 +13,7 @@
|
|||
"@alexbosworth/blockchain": "3.2.0",
|
||||
"@alexbosworth/caporal": "2.0.0",
|
||||
"@alexbosworth/fiat": "2.0.0",
|
||||
"@alexbosworth/formulas": "1.4.0",
|
||||
"@alexbosworth/html2unicode": "1.1.5",
|
||||
"@alexbosworth/node-fetch": "2.6.2",
|
||||
"abort-controller": "3.0.0",
|
||||
|
|
@ -29,19 +30,18 @@
|
|||
"colorette": "2.0.20",
|
||||
"crypto-js": "4.2.0",
|
||||
"csv-parse": "7.0.1",
|
||||
"goldengate": "16.0.3",
|
||||
"grammy": "1.44.0",
|
||||
"hot-formula-parser": "4.0.0",
|
||||
"goldengate": "16.0.4",
|
||||
"grammy": "1.45.1",
|
||||
"import-lazy": "4.0.0",
|
||||
"ini": "7.0.0",
|
||||
"inquirer": "14.0.2",
|
||||
"ln-accounting": "10.0.3",
|
||||
"ln-service": "59.2.0",
|
||||
"ln-accounting": "10.0.4",
|
||||
"ln-service": "59.2.2",
|
||||
"ln-sync": "8.0.3",
|
||||
"ln-telegram": "8.0.3",
|
||||
"ln-telegram": "8.0.4",
|
||||
"minimist": "1.2.8",
|
||||
"moment": "2.30.1",
|
||||
"paid-services": "8.0.3",
|
||||
"paid-services": "8.0.4",
|
||||
"probing": "7.0.0",
|
||||
"qrcode-terminal": "0.12.0",
|
||||
"sanitize-filename": "1.6.4",
|
||||
|
|
@ -78,7 +78,7 @@
|
|||
"integration-tests": "node test/integration",
|
||||
"postpack": "PACKAGE_VERSION=$(cat package.json | grep \\\"version\\\" | head -1 | awk -F: '{ print $2 }' | sed 's/[\",]//g' | tr -d '[[:space:]]') && git tag -s v$PACKAGE_VERSION -m v$PACKAGE_VERSION && git push github --tags",
|
||||
"postpublish": "docker buildx build --platform linux/amd64,linux/arm64 -t alexbosworth/balanceofsatoshis -t alexbosworth/balanceofsatoshis:$npm_package_version --push .",
|
||||
"test": "npx nyc@17.1.0 node --experimental-test-coverage --test 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"
|
||||
"test": "node --experimental-test-coverage --test 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": "22.1.7"
|
||||
"version": "23.0.0"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ const tok = n => Number(BigInt(n) / BigInt(1e3));
|
|||
|
||||
{
|
||||
channels: [{
|
||||
capacity: <Channel Token Capacity Number>
|
||||
id: <Standard Format Channel Id String>
|
||||
local_balance: <Channel Local Balance Tokens Number>
|
||||
partner_public_key: <Peer Public Key Hex String>
|
||||
|
|
@ -97,20 +98,25 @@ module.exports = ({channels, filters, policies, tags, query}) => {
|
|||
const feeRates = peerPolicies.filter(n => n.fee_rate !== undefined);
|
||||
const pendingPayments = withPeer.map(n => n.pending_payments.length);
|
||||
|
||||
const matching = isMatchingFilters({
|
||||
filters: filters || [],
|
||||
variables: {
|
||||
capacity: sumOf(withPeer.map(n => n.capacity)),
|
||||
heights: withPeer.map(n => {
|
||||
return decodeChanId({channel: n.id}).block_height;
|
||||
}),
|
||||
inbound_base_fee: max(...feeRates.map(n => tok(n.base_fee_mtokens))),
|
||||
inbound_fee_rate: max(...feeRates.map(n => n.fee_rate)),
|
||||
inbound_liquidity: sumOf(withPeer.map(n => n.remote_balance)),
|
||||
outbound_liquidity: sumOf(withPeer.map(n => n.local_balance)),
|
||||
pending_payments: sumOf(pendingPayments),
|
||||
},
|
||||
});
|
||||
const maxBaseFee = max(...feeRates.map(n => tok(n.base_fee_mtokens)));
|
||||
const maxFeeRate = max(...feeRates.map(n => n.fee_rate));
|
||||
|
||||
const variables = {
|
||||
capacity: sumOf(withPeer.map(n => n.capacity)),
|
||||
heights: withPeer.map(n => {
|
||||
return decodeChanId({channel: n.id}).block_height;
|
||||
}),
|
||||
inbound_liquidity: sumOf(withPeer.map(n => n.remote_balance)),
|
||||
outbound_liquidity: sumOf(withPeer.map(n => n.local_balance)),
|
||||
pending_payments: sumOf(pendingPayments),
|
||||
};
|
||||
|
||||
if (!!feeRates.length) {
|
||||
variables.inbound_base_fee = maxBaseFee;
|
||||
variables.inbound_fee_rate = maxFeeRate;
|
||||
}
|
||||
|
||||
const matching = isMatchingFilters({variables, filters: filters || []});
|
||||
|
||||
if (!!matching.failure) {
|
||||
return matching;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
const {Parser} = require('hot-formula-parser');
|
||||
const {evaluateFormula} = require('@alexbosworth/formulas');
|
||||
|
||||
const {isArray} = Array;
|
||||
const {keys} = Object;
|
||||
|
|
@ -72,7 +72,7 @@ module.exports = args => {
|
|||
throw new Error('ExpectedTorStatusToCheckOpenRequestRules');
|
||||
}
|
||||
|
||||
const variables = {
|
||||
const constants = {
|
||||
btc: 1e8,
|
||||
capacities: args.capacities,
|
||||
capacity: args.capacity,
|
||||
|
|
@ -84,46 +84,14 @@ module.exports = args => {
|
|||
local_balance: args.local_balance,
|
||||
m: 1e6,
|
||||
mm: 1e6,
|
||||
obsolete: args.is_obsolete,
|
||||
obsolete: !!args.is_obsolete,
|
||||
private: args.is_private,
|
||||
public_key: args.public_key,
|
||||
tor: args.is_tor,
|
||||
};
|
||||
|
||||
const parser = new Parser();
|
||||
|
||||
// Add the variables to the parser
|
||||
keys(variables).forEach(key => {
|
||||
parser.setVariable(key.toLowerCase(), variables[key]);
|
||||
parser.setVariable(key.toUpperCase(), variables[key]);
|
||||
|
||||
return;
|
||||
});
|
||||
|
||||
const violation = args.rules.find(rule => {
|
||||
const parsed = parser.parse(rule);
|
||||
|
||||
switch (parsed.error) {
|
||||
case '#DIV/0!':
|
||||
throw new Error('CannotDivideByZeroInOpenRequestRule');
|
||||
|
||||
case '#ERROR!':
|
||||
throw new Error('FailedToParseSpecifiedOpenRequestRule');
|
||||
|
||||
case '#N/A':
|
||||
case '#NAME?':
|
||||
throw new Error('UnrecognizedVariableOrFunctionInRequestRule');
|
||||
|
||||
case '#NUM':
|
||||
throw new Error('InvalidNumberFoundInRequestRule');
|
||||
|
||||
case '#VALUE!':
|
||||
throw new Error('UnexpectedValueTypeInRequestRule');
|
||||
|
||||
default:
|
||||
// Rules must evaluate as truthy
|
||||
return !parsed.result;
|
||||
}
|
||||
return !evaluateFormula({constants, formula: rule}).result;
|
||||
});
|
||||
|
||||
return {rule: violation};
|
||||
|
|
|
|||
|
|
@ -1,18 +1,15 @@
|
|||
const asyncAuto = require('async/auto');
|
||||
const asyncMap = require('async/map');
|
||||
const {evaluateFormula} = require('@alexbosworth/formulas');
|
||||
const {findKey} = require('ln-sync');
|
||||
const {getChannel} = require('ln-service');
|
||||
const {getHeight} = require('ln-service');
|
||||
const {getNode} = require('ln-service');
|
||||
const {Parser} = require('hot-formula-parser');
|
||||
const {returnResult} = require('asyncjs-util');
|
||||
|
||||
const {describeParseError} = require('./../display');
|
||||
|
||||
const amountVariables = {btc: 1e8, k: 1e3, m: 1e6, mm: 1e6};
|
||||
const asFormula = n => ({formula: n.slice(0, n.length-67), key: n.slice(-66)});
|
||||
const asOutFilter = n => ({out_filter: n.slice(67), key: n.slice(0, 66)});
|
||||
const {assign} = Object;
|
||||
const channelFromEdge = edge => edge.slice(0, -2);
|
||||
const decodePair = n => n.split('/');
|
||||
const flatten = arr => [].concat(...arr);
|
||||
|
|
@ -25,7 +22,6 @@ const isFormula = n => /(.*)\/0[2-3][0-9A-F]{64}$/gim.test(n);
|
|||
const isOutFilter = n => /^0[2-3][0-9A-F]{64}\/(.*)/gim.test(n);
|
||||
const isPair = n => !!n && /^0[2-3][0-9A-F]{64}\/0[2-3][0-9A-F]{64}$/i.test(n);
|
||||
const isPublicKey = n => !!n && /^0[2-3][0-9A-F]{64}$/gim.test(n);
|
||||
const {keys} = Object;
|
||||
const pairAsIgnore = (a, b) => ({from_public_key: a, to_public_key: b});
|
||||
const uniq = arr => Array.from(new Set(arr));
|
||||
|
||||
|
|
@ -255,35 +251,25 @@ module.exports = (args, cbk) => {
|
|||
return;
|
||||
}
|
||||
|
||||
const parser = new Parser();
|
||||
const variables = {};
|
||||
try {
|
||||
const parsed = evaluateFormula({
|
||||
formula,
|
||||
constants: {
|
||||
...amountVariables,
|
||||
capacity,
|
||||
height,
|
||||
age: getHeight.current_block_height - height,
|
||||
base_fee: Number(outPolicy.base_fee_mtokens) || Number(),
|
||||
fee_rate: outPolicy.fee_rate || Number(),
|
||||
opposite_fee_rate: peerPolicy.fee_rate || Number(),
|
||||
},
|
||||
});
|
||||
|
||||
assign(variables, amountVariables);
|
||||
|
||||
assign(variables, {
|
||||
capacity,
|
||||
height,
|
||||
age: getHeight.current_block_height - height,
|
||||
base_fee: Number(outPolicy.base_fee_mtokens) || Number(),
|
||||
fee_rate: outPolicy.fee_rate || Number(),
|
||||
opposite_fee_rate: peerPolicy.fee_rate || Number(),
|
||||
});
|
||||
|
||||
keys(variables).forEach(key => {
|
||||
parser.setVariable(key.toLowerCase(), variables[key]);
|
||||
parser.setVariable(key.toUpperCase(), variables[key]);
|
||||
|
||||
return;
|
||||
});
|
||||
|
||||
const parsed = parser.parse(formula);
|
||||
|
||||
if (!!parsed.error) {
|
||||
return {error: describeParseError({error: parsed.error})};
|
||||
}
|
||||
|
||||
if (parsed.result === false) {
|
||||
return;
|
||||
if (!parsed.result) {
|
||||
return;
|
||||
}
|
||||
} catch (err) {
|
||||
return {error: err.message};
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
@ -333,35 +319,25 @@ module.exports = (args, cbk) => {
|
|||
return;
|
||||
}
|
||||
|
||||
const parser = new Parser();
|
||||
const variables = {};
|
||||
try {
|
||||
const parsed = evaluateFormula({
|
||||
formula,
|
||||
constants: {
|
||||
...amountVariables,
|
||||
capacity,
|
||||
height,
|
||||
age: getHeight.current_block_height - height,
|
||||
base_fee: Number(inPolicy.base_fee_mtokens) || Number(),
|
||||
fee_rate: inPolicy.fee_rate || Number(),
|
||||
opposite_fee_rate: outPolicy.fee_rate || Number(),
|
||||
},
|
||||
});
|
||||
|
||||
assign(variables, amountVariables);
|
||||
|
||||
assign(variables, {
|
||||
capacity,
|
||||
height,
|
||||
age: getHeight.current_block_height - height,
|
||||
base_fee: Number(inPolicy.base_fee_mtokens) || Number(),
|
||||
fee_rate: inPolicy.fee_rate || Number(),
|
||||
opposite_fee_rate: outPolicy.fee_rate || Number(),
|
||||
});
|
||||
|
||||
keys(variables).forEach(key => {
|
||||
parser.setVariable(key.toLowerCase(), variables[key]);
|
||||
parser.setVariable(key.toUpperCase(), variables[key]);
|
||||
|
||||
return;
|
||||
});
|
||||
|
||||
const parsed = parser.parse(formula);
|
||||
|
||||
if (!!parsed.error) {
|
||||
return {error: describeParseError({error: parsed.error})};
|
||||
}
|
||||
|
||||
if (parsed.result === false) {
|
||||
return;
|
||||
if (!parsed.result) {
|
||||
return;
|
||||
}
|
||||
} catch (err) {
|
||||
return {error: err.message};
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
const {Parser} = require('hot-formula-parser');
|
||||
const {evaluateFormula} = require('@alexbosworth/formulas');
|
||||
|
||||
const bipsAsPpm = bips => bips * 1e2;
|
||||
const {ceil} = Math;
|
||||
|
|
@ -12,7 +12,7 @@ const percentAsPpm = percent => percent * 1e4;
|
|||
inbound_liquidity: <Inbound Tokens Number>
|
||||
outbound_liquidity: <Outbound Tokens Number>
|
||||
node_rates: [{
|
||||
key: <Node Key String>
|
||||
key: <Label Prefixed Node Key String>
|
||||
rate: <Node PPM Rate Number>
|
||||
}]
|
||||
}
|
||||
|
|
@ -28,31 +28,30 @@ module.exports = args => {
|
|||
return {};
|
||||
}
|
||||
|
||||
const parser = new Parser();
|
||||
const rates = args.node_rates.reduce((sum, n) => {
|
||||
sum[n.key] = n.rate;
|
||||
|
||||
parser.setFunction('BIPS', params => bipsAsPpm(params.slice().pop()));
|
||||
parser.setFunction('PERCENT', params => percentAsPpm(params.slice().pop()));
|
||||
parser.setVariable('INBOUND', args.inbound_liquidity);
|
||||
parser.setVariable('INBOUND_FEE_RATE', args.inbound_fee_rate);
|
||||
parser.setVariable('OUTBOUND', args.outbound_liquidity);
|
||||
return sum;
|
||||
},
|
||||
{});
|
||||
|
||||
args.node_rates.forEach(({key, rate}) => parser.setVariable(key, rate));
|
||||
try {
|
||||
const {result} = evaluateFormula({
|
||||
constants: {
|
||||
...rates,
|
||||
inbound: args.inbound_liquidity,
|
||||
inbound_fee_rate: args.inbound_fee_rate,
|
||||
outbound: args.outbound_liquidity,
|
||||
},
|
||||
formula: args.fee_rate,
|
||||
functions: {
|
||||
bips: bipsAsPpm,
|
||||
percent: percentAsPpm,
|
||||
},
|
||||
});
|
||||
|
||||
const parsedRate = parser.parse(args.fee_rate.toUpperCase());
|
||||
|
||||
switch (parsedRate.error) {
|
||||
case null:
|
||||
break;
|
||||
|
||||
case '#DIV/0!':
|
||||
return {failure: 'FeeRateCalculationCannotDivideByZeroFormula'};
|
||||
|
||||
case '#ERROR!':
|
||||
return {failure: 'FailedToParseFeeRateFormula'};
|
||||
|
||||
default:
|
||||
return {failure: 'UnrecognizedVariableOrFunctionInFeeRateFormula'};
|
||||
return {rate: ceil(result)};
|
||||
} catch (err) {
|
||||
return {failure: err.message};
|
||||
}
|
||||
|
||||
return {rate: ceil(parsedRate.result)};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ const tests = [
|
|||
description: 'An invalid filter returns a failure',
|
||||
expected: {
|
||||
failure: {
|
||||
error: 'UnrecognizedVariableOrFunctionInFormula',
|
||||
error: 'ExpectedAllKnownConstantsInFormulaToEvaluate',
|
||||
formula: 'foo',
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ const {findTagMatch} = require('./../../peers');
|
|||
const makeArgs = overrides => {
|
||||
const args = {
|
||||
channels: [{
|
||||
capacity: 3,
|
||||
id: '1x1x1',
|
||||
local_balance: 1,
|
||||
partner_public_key: Buffer.alloc(33, 3).toString('hex'),
|
||||
|
|
@ -36,13 +37,17 @@ const tests = [
|
|||
args: makeArgs({filters: ['invalid formula']}),
|
||||
description: 'A failed formula is provided',
|
||||
expected: {
|
||||
failure: {error: 'FailedToParseFormula', formula: 'invalid formula'},
|
||||
failure: {
|
||||
error: 'UnexpectedTrailingTokenForFormulaParsing',
|
||||
formula: 'invalid formula',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
args: makeArgs({
|
||||
channels: [
|
||||
{
|
||||
capacity: 2e8,
|
||||
id: '1x1x1',
|
||||
local_balance: 1e3,
|
||||
partner_public_key: Buffer.alloc(33, 3).toString('hex'),
|
||||
|
|
@ -50,6 +55,7 @@ const tests = [
|
|||
remote_balance: 1,
|
||||
},
|
||||
{
|
||||
capacity: 2e8,
|
||||
id: '2x2x2',
|
||||
local_balance: 1e8,
|
||||
partner_public_key: Buffer.alloc(33, 2).toString('hex'),
|
||||
|
|
|
|||
|
|
@ -42,17 +42,17 @@ const tests = [
|
|||
{
|
||||
args: makeArgs({fee_rate: '1/0'}),
|
||||
description: 'Cannot divide by zero',
|
||||
expected: {failure: 'FeeRateCalculationCannotDivideByZeroFormula'},
|
||||
expected: {failure: 'ExpectedNonZeroDivisorForFormulaEvaluation'},
|
||||
},
|
||||
{
|
||||
args: makeArgs({fee_rate: '/'}),
|
||||
description: 'Formula must be valid',
|
||||
expected: {failure: 'FailedToParseFeeRateFormula'},
|
||||
expected: {failure: 'UnexpectedPrimaryTokenForFormulaParsing'},
|
||||
},
|
||||
{
|
||||
args: makeArgs({fee_rate: 'fee_rate'}),
|
||||
description: 'Formula must be valid',
|
||||
expected: {failure: 'UnrecognizedVariableOrFunctionInFeeRateFormula'},
|
||||
expected: {failure: 'ExpectedAllKnownConstantsInFormulaToEvaluate'},
|
||||
},
|
||||
];
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue