add joint capacity to formula variables

This commit is contained in:
Alex Bosworth 2023-04-29 15:13:14 -07:00
parent 40c808cfb7
commit 9c31a9bfa9
No known key found for this signature in database
GPG key ID: E80D2F3F311FD87E
9 changed files with 70 additions and 38 deletions

View file

@ -1,5 +1,10 @@
# Versions
## 15.4.0
- `inbound-channel-rules`: Add `JOINT_PUBLIC_CAPACITY` for rules based on
existing joint capacity
## 15.3.0
- `inbound-channel-rules`: Add support for `--stop` to stop forwards on channel

70
bos
View file

@ -877,6 +877,7 @@ prog
.command('inbound-channel-rules', 'Enforce rules for inbound channels')
.help('Rules should be written evaluating to TRUE to accept a channel')
.help('Example rule: --rule "CAPACITY > 100000"')
.help('You can use multiple --rule flags to enforce multiple rules')
.help('Multiple --coop-close-address supported, will cycle through them')
.help('Avoid requiring Blockchain confirmations using --trust-funding-from')
.help('For formulas: CAPACITIES are the sizes of the peer public channels')
@ -884,6 +885,7 @@ prog
.help('For formulas: CHANNEL_AGES are the block ages of public channels')
.help('For formulas: CLEARNET is when peer advertises a clearnet address')
.help('For formulas: FEE_RATES are the outbound fee rates for the peer')
.help('For formulas: JOINT_PUBLIC_CAPACITY is graph capacity with the peer')
.help('For formulas: LOCAL_BALANCE is the gifted amount from the peer')
.help('For formulas: M is 1,000,000 - use like CAPACITY > 10*M')
.help('For formulas: OBSOLETE is chan type, like NOT(OBSOLETE)')
@ -914,40 +916,6 @@ prog
});
})
// Intercept forwarding requests, enforce requirements on acceptance
.command('limit-forwarding', 'Enforce rules for routing payments')
.help(`--deny option can be repeated for multiple denied from/to paths`)
.help('Setting --only-allow will disable all forwards except only allowed')
.help('--only-allow option can be repeated for multiple forwards')
.help('--stop can be repeated for multiple channels')
.option('--deny <pair>', 'disable all forwards fromKey/toKey', REPEATABLE)
.option('--disable-forwards', 'Disable all forwards')
.option('--max-hours-since-last-block <h>', 'Require fresh blocks', INT, 5)
.option('--max-new-pending-per-hour <h>', 'Limit held HTLCs', INT)
.option('--min-channel-confirmations <confs>', 'Minimum channel confs', INT)
.option('--node <node_name>', 'Saved node to enforce rules on')
.option('--only-allow <pair>', 'only forward fromKey/toKey', REPEATABLE)
.option('--stop <channel>', 'Stop new forwards on channel', REPEATABLE)
.action((args, options, logger) => {
return new Promise(async (resolve, reject) => {
try {
return await peers.limitForwarding({
logger,
is_disabling_all_forwards: options.disableForwards || undefined,
lnd: (await lndForNode(logger, options.node)).lnd,
max_hours_since_last_block: options.maxHoursSinceLastBlock,
max_new_pending_per_hour: options.maxNewPendingPerHour,
min_channel_confirmations: options.minChannelConfirmations,
only_allow: flatten([options.onlyAllow].filter(n => !!n)),
only_disallow: flatten([options.deny].filter(n => !!n)),
stop_channels: flatten([options.stop].filter(n => !!n)),
});
} catch (err) {
return logger.error({err}) && reject();
}
});
})
// Get inbound liquidity information: available inbound off-chain tokens
.command('inbound-liquidity', 'Get inbound liquidity size')
.option('--above <tokens>', 'Return amount above watermark', INT)
@ -1138,6 +1106,40 @@ prog
});
})
// Intercept forwarding requests, enforce requirements on acceptance
.command('limit-forwarding', 'Enforce rules for routing payments')
.help(`--deny option can be repeated for multiple denied from/to paths`)
.help('Setting --only-allow will disable all forwards except only allowed')
.help('--only-allow option can be repeated for multiple forwards')
.help('--stop can be repeated for multiple channels')
.option('--deny <pair>', 'disable all forwards fromKey/toKey', REPEATABLE)
.option('--disable-forwards', 'Disable all forwards')
.option('--max-hours-since-last-block <h>', 'Require fresh blocks', INT, 5)
.option('--max-new-pending-per-hour <h>', 'Limit held HTLCs', INT)
.option('--min-channel-confirmations <confs>', 'Minimum channel confs', INT)
.option('--node <node_name>', 'Saved node to enforce rules on')
.option('--only-allow <pair>', 'only forward fromKey/toKey', REPEATABLE)
.option('--stop <channel>', 'Stop new forwards on channel', REPEATABLE)
.action((args, options, logger) => {
return new Promise(async (resolve, reject) => {
try {
return await peers.limitForwarding({
logger,
is_disabling_all_forwards: options.disableForwards || undefined,
lnd: (await lndForNode(logger, options.node)).lnd,
max_hours_since_last_block: options.maxHoursSinceLastBlock,
max_new_pending_per_hour: options.maxNewPendingPerHour,
min_channel_confirmations: options.minChannelConfirmations,
only_allow: flatten([options.onlyAllow].filter(n => !!n)),
only_disallow: flatten([options.deny].filter(n => !!n)),
stop_channels: flatten([options.stop].filter(n => !!n)),
});
} catch (err) {
return logger.error({err}) && reject();
}
});
})
// Get the price for liquidity
.command('liquidity-cost', 'Get the price of liquidity')
.visible(false)

4
package-lock.json generated
View file

@ -1,12 +1,12 @@
{
"name": "balanceofsatoshis",
"version": "15.3.0",
"version": "15.4.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "balanceofsatoshis",
"version": "15.3.0",
"version": "15.4.0",
"license": "MIT",
"dependencies": {
"@alexbosworth/caporal": "1.4.4",

View file

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

View file

@ -10,11 +10,13 @@ const isClear = sockets => !!sockets.find(n => !!isIP(n.socket.split(':')[0]));
const isObsolete = n => !!n && n.startsWith('original');
const isOnion = sockets => !!sockets.find(n => /onion/.test(n.socket));
const openRequestViolation = require('./open_request_violation');
const sumOf = arr => arr.reduce((sum, n) => sum + n, 0);
/** Detect an open request rule violation
{
capacity: <Channel Capacity Tokens Number>
id: <Identity Public Key Hex String>
[is_private]: <Channel Request Is For Private Channel Bool>
lnd: <Authenticated LND API Object>
local_balance: <Local Channel Balance Tokens Number>
@ -37,6 +39,10 @@ module.exports = (args, cbk) => {
return cbk([400, 'ExpectedChannelCapacityToDetectRuleViolation']);
}
if (!args.id) {
return cbk([400, 'ExpectedIdentityPublicKeyToDetectRuleViolation']);
}
if (!args.lnd) {
return cbk([400, 'ExpectedLndToDetectOpenChannelRuleViolation']);
}
@ -125,6 +131,11 @@ module.exports = (args, cbk) => {
return getHeight.current_block_height - channelHeight;
});
// Filter out channels that already exist
const jointChannels = getNodeFees.channels.filter(({policies}) => {
return policies.find(n => n.public_key === args.id);
});
try {
const {rule} = openRequestViolation({
capacities: getNodeFees.channels.map(n => n.capacity),
@ -139,6 +150,7 @@ module.exports = (args, cbk) => {
is_obsolete: isObsolete(args.type),
is_private: !!args.is_private,
is_tor: hasTor,
joint_public_capacity: sumOf(jointChannels.map(n => n.capacity)),
public_key: args.partner_public_key,
rules: args.rules,
});

View file

@ -1,4 +1,5 @@
const asyncAuto = require('async/auto');
const {getIdentity} = require('ln-service');
const {getNetwork} = require('ln-sync');
const {returnResult} = require('asyncjs-util');
const {subscribeToOpenRequests} = require('ln-service');
@ -65,6 +66,7 @@ module.exports = ({addresses, lnd, logger, reason, rules, trust}, cbk) => {
is_obsolete: false,
is_private: false,
is_tor: false,
joint_public_capacity: 1,
local_balance: 4,
public_key: Buffer.alloc(33, 2).toString('hex'),
});
@ -111,8 +113,11 @@ module.exports = ({addresses, lnd, logger, reason, rules, trust}, cbk) => {
});
}],
// Get the node key identity
getIdentity: ['validate', ({}, cbk) => getIdentity({lnd}, cbk)],
// Subscribe to open requests
subscribe: ['checkAddress', ({}, cbk) => {
subscribe: ['checkAddress', 'getIdentity', ({getIdentity}, cbk) => {
const sub = subscribeToOpenRequests({lnd});
// Copy the addresses into a pool
@ -148,6 +153,7 @@ module.exports = ({addresses, lnd, logger, reason, rules, trust}, cbk) => {
lnd,
rules,
capacity: request.capacity,
id: getIdentity.public_key,
is_private: request.is_private,
local_balance: request.local_balance,
partner_public_key: peerId,

View file

@ -52,6 +52,10 @@ module.exports = args => {
throw new Error('ExpectedChannelPrivateStatusToCheckOpenRequestRules');
}
if (args.joint_public_capacity === undefined) {
throw new Error('ExpectedJointPublicCapacityToCheckOpenRequestRules');
}
if (args.local_balance === undefined) {
throw new Error('ExpectedLocalBalanceToCheckForOpenRequestViolation');
}
@ -75,6 +79,7 @@ module.exports = args => {
channel_ages: args.channel_ages,
clearnet: args.is_clearnet,
fee_rates: args.fee_rates,
joint_public_capacity: args.joint_public_capacity,
k: 1e3,
local_balance: args.local_balance,
m: 1e6,

View file

@ -10,6 +10,7 @@ const {versionInfoResponse} = require('./../fixtures');
const makeArgs = overrides => {
const args = {
capacity: 1,
id: Buffer.alloc(33, 2),
lnd: {
chain: {
registerBlockEpochNtfn: ({}) => {

View file

@ -11,6 +11,7 @@ const makeArgs = overrides => {
is_clearnet: true,
is_private: true,
is_tor: true,
joint_public_capacity: 1,
local_balance: 7,
public_key: Buffer.alloc(33, 3).toString('hex'),
rules: ['capacity > 5'],