Introducing: inbound_level_ppm and min_inbound_fee_ppm_delta

1) `min_inbound_fee_ppm_delta` is the necessary minimum change in the
   inbound_fee_ppm. The parameter uses `min_fee_ppm_delta` as the default.
2) We also ensure that the inbound fee rates are only changed if the
   change is sufficiently large.
3) If `inbound_level_ppm` is set, `inbound_fee_ppm` is calculated
   with `min(inbound_level_ppm - fee_ppm; 0)`.
This commit is contained in:
feelancer21 2024-06-04 18:11:53 +02:00
parent fd1a164acf
commit 7f651450a9
No known key found for this signature in database
GPG key ID: 1F7071EE8449729C
3 changed files with 41 additions and 17 deletions

View file

@ -213,11 +213,11 @@ Available strategies:
|:--|:--|:--|
|**ignore** | ignores the channel completely||
|**ignore_fees** | don't make any fee changes, only update htlc size limits and time_lock_delta||
|**static** | sets fixed base fee and fee rate values for the outbound and inbound side.| **fee_ppm**<br>**base_fee_msat**<br>**inbound_fee_ppm**<br>**inbound_base_fee_msat**|
|**static** | sets fixed base fee and fee rate values for the outbound and inbound side.| **fee_ppm**<br>**base_fee_msat**<br>**inbound_fee_ppm**<br>**inbound_base_fee_msat**<br>**inbound_level_ppm** if set we calculate `inbound_fee_ppm = min(0,inbound_level_ppm - fee_ppm)`|
|**match_peer** | sets the same base fee and fee rate values as the peer for the outbound and inbound side.|if **base_fee_msat**, **fee_ppm**, **inbound_base_fee_msat** or **inbound_fee_ppm** are set the override the peer values|
|**cost** | calculate cost for opening channel, and set ppm to cover cost when channel depletes.|**cost_factor**|
|**onchain_fee** | sets the fees to a % equivalent of a standard onchain payment. We use lnd's internal fee estimate, which is usually based on bitcoind's fee estimate.| **onchain_fee_btc** BTC<br>within **onchain_fee_numblocks** blocks.|
|**proportional** | sets outbound fee ppm according to balancedness. Inbound fee ppm keeps unchanged.|**min_fee_ppm**<br>**max_fee_ppm**<br>**sum_peer_chans** consider all channels with peer for balance calculations|
|**proportional** | sets outbound fee ppm according to balancedness. Inbound Fees are set like using strategy **static**.|**min_fee_ppm**<br>**max_fee_ppm**<br>**sum_peer_chans** consider all channels with peer for balance calculations|
|**disable** | disables the channel in the outgoing direction. Channel will be re-enabled again if it matches another policy (except when that policy uses an 'ignore' strategy).||
|**use_config** | process channel according to rules defined in another config file.|**config_file**|
@ -230,7 +230,8 @@ All strategies (except the ignore strategy) will apply the following properties
| **max_htlc_msat** | Maximum size (in msat) of HTLC to allow | # msat |
| **max_htlc_msat_ratio** | Maximum size of HTLC to allow as a fraction of total channel capacity | 0..1 |
| **time_lock_delta** | Time Lock Delta | # blocks |
| **min_fee_ppm_delta** | Minimum change in fees (ppm) before updating channel | ppm delta |
| **min_fee_ppm_delta** | Minimum change in fees (ppm) before updating channel (default: 0) | ppm delta |
| **min_inbound_fee_ppm_delta** | Minimum change in inbound fees (ppm) before updating channel (default: min_fee_ppm_delta) | ppm delta |
| **cb_max_hourly_rate** | Circuitbreaker: maximum number of incoming htlcs per hour | # hourly rate |
| **cb_max_pending** | Circuitbreaker: maximum number of incoming htlcs at the same time | # incoming pending htlcs |
| **cb_mode** | Circuitbreaker: mode (0 - FAIL; 1 - QUEUE; 2 - QUEUE_PEER_INITIATED; 3 - BLOCK) | 0..3 |

View file

@ -79,14 +79,32 @@ def main():
fee_ppm_changed = is_defined(chp.fee_ppm) and current_fee_ppm != chp.fee_ppm and abs(current_fee_ppm - chp.fee_ppm) >= min_fee_ppm_delta
base_fee_changed = is_defined(chp.base_fee_msat) and current_base_fee_msat != chp.base_fee_msat
inbound_fee_ppm_changed = lnd.supports_inbound_fees() \
and is_defined(chp.inbound_fee_ppm) \
and my_policy.inbound_fee_rate_milli_msat != chp.inbound_fee_ppm \
and abs(my_policy.inbound_fee_rate_milli_msat - chp.inbound_fee_ppm) >= min_fee_ppm_delta
min_inbound_fee_ppm_delta = policy.getint('min_inbound_fee_ppm_delta', min_fee_ppm_delta)
inbound_fee_ppm_changed = inbound_base_fee_changed = False
if lnd.supports_inbound_fees():
# If there is any definied inbound_level we recalculate the inbound fee rate.
if is_defined(chp.inbound_level_ppm):
chp.inbound_fee_ppm = min(0,
chp.inbound_level_ppm - (chp.fee_ppm if fee_ppm_changed else current_fee_ppm))
inbound_base_fee_changed = lnd.supports_inbound_fees() \
and is_defined(chp.inbound_base_fee_msat) \
and my_policy.inbound_fee_base_msat != chp.inbound_base_fee_msat
# We'd like to avoid updating the inbound fee rate if the change is lower than the min delta,
# even in the case other outbound properties have changed.
# The goal is to minimize the gossip around the inbound fees, because at the moment there
# are no updates for the incoming channel in the case of a FeeInsuffient error.
# (https://github.com/lightningnetwork/lnd/pull/6967)
# First we check if the base fee changed. In this case we will advance the inbound
# fee anyway.
inbound_base_fee_changed = is_defined(chp.inbound_base_fee_msat) \
and my_policy.inbound_fee_base_msat != chp.inbound_base_fee_msat
if is_defined(chp.inbound_fee_ppm) \
and not inbound_base_fee_changed \
and abs(my_policy.inbound_fee_rate_milli_msat - chp.inbound_fee_ppm) < min_inbound_fee_ppm_delta:
chp.inbound_fee_ppm = my_policy.inbound_fee_rate_milli_msat
inbound_fee_ppm_changed = is_defined(chp.inbound_fee_ppm) \
and my_policy.inbound_fee_rate_milli_msat != chp.inbound_fee_ppm
# We are using the local constraints as a floor for min_htlc and as a cap for max_htlc.
# Otherwise, e.g. if min_htlc is too low, lnd cannot perform the whole policy update.
@ -94,7 +112,7 @@ def main():
chp.min_htlc_msat = max(chp.min_htlc_msat, channel.local_constraints.min_htlc_msat)
if is_defined(chp.max_htlc_msat):
chp.max_htlc_msat = min(chp.max_htlc_msat, channel.local_constraints.max_pending_amt_msat)
min_htlc_changed = is_defined(chp.min_htlc_msat) and my_policy.min_htlc != chp.min_htlc_msat
max_htlc_changed = is_defined(chp.max_htlc_msat) and my_policy.max_htlc_msat != chp.max_htlc_msat
time_lock_delta_changed = is_defined(chp.time_lock_delta) and my_policy.time_lock_delta != chp.time_lock_delta
@ -146,8 +164,8 @@ def main():
s = ''
if inbound_fee_ppm_changed:
s = '' + fmt.col_hi(chp.inbound_fee_ppm)
if min_fee_ppm_delta > abs(chp.inbound_fee_ppm - my_policy.inbound_fee_rate_milli_msat):
s = s + ' (min_fee_ppm_delta=%d)' % min_fee_ppm_delta
if min_inbound_fee_ppm_delta > abs(chp.inbound_fee_ppm - my_policy.inbound_fee_rate_milli_msat):
s = s + ' (min_inbound_fee_ppm_delta=%d)' % min_inbound_fee_ppm_delta
print(" inbound_fee_ppm: %s%s" % (fmt.col_hi(my_policy.inbound_fee_rate_milli_msat), s) )
if is_defined(chp.min_htlc_msat) or arguments.verbose:
s = ''

View file

@ -26,6 +26,7 @@ class ChanParams(SimpleNamespace):
time_lock_delta: Optional[Union[str, int]] = DONTCARE
inbound_base_fee_msat: Optional[Union[str, int]] = DONTCARE
inbound_fee_ppm: Optional[Union[str, int]] = DONTCARE
inbound_level_ppm: Optional[Union[str, int]] = DONTCARE
disabled: Optional[Union[str, bool]] = DONTCARE
circuitbreaker_params: Optional[Union[str, CircuitbreakerParams]] = DONTCARE
@ -115,7 +116,8 @@ def strategy_static(channel, policy, **kwargs):
base_fee_msat=policy.getint('base_fee_msat'),
fee_ppm=policy.getint('fee_ppm'),
inbound_base_fee_msat=policy.getint('inbound_base_fee_msat'),
inbound_fee_ppm=policy.getint('inbound_fee_ppm')
inbound_fee_ppm=policy.getint('inbound_fee_ppm'),
inbound_level_ppm=policy.getint('inbound_level_ppm'),
)
@strategy(name = 'proportional')
@ -158,7 +160,8 @@ def strategy_proportional(channel, policy, **kwargs):
base_fee_msat=policy.getint('base_fee_msat'),
fee_ppm=ppm,
inbound_base_fee_msat=policy.getint('inbound_base_fee_msat'),
inbound_fee_ppm=policy.getint('inbound_fee_ppm')
inbound_fee_ppm=policy.getint('inbound_fee_ppm'),
inbound_level_ppm=policy.getint('inbound_level_ppm'),
)
@strategy(name = 'match_peer')
@ -197,7 +200,8 @@ def strategy_cost(channel, policy, **kwargs):
base_fee_msat=policy.getint('base_fee_msat'),
fee_ppm=ppm,
inbound_base_fee_msat=policy.getint('inbound_base_fee_msat'),
inbound_fee_ppm=policy.getint('inbound_fee_ppm')
inbound_fee_ppm=policy.getint('inbound_fee_ppm'),
inbound_level_ppm=policy.getint('inbound_level_ppm'),
)
@ -218,7 +222,8 @@ def strategy_onchain_fee(channel, policy, **kwargs):
base_fee_msat=policy.getint('base_fee_msat'),
fee_ppm=fee_ppm,
inbound_base_fee_msat=policy.getint('inbound_base_fee_msat'),
inbound_fee_ppm=policy.getint('inbound_fee_ppm')
inbound_fee_ppm=policy.getint('inbound_fee_ppm'),
inbound_level_ppm=policy.getint('inbound_level_ppm'),
)