Skip to content

Commit

Permalink
Merge pull request #110 from feelancer21/inbound_fees
Browse files Browse the repository at this point in the history
Inbound fees
  • Loading branch information
accumulator authored May 15, 2024
2 parents db9f292 + 6372dc2 commit 14cb8a0
Show file tree
Hide file tree
Showing 13 changed files with 4,656 additions and 17,997 deletions.
22 changes: 19 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,20 @@ This policy matches the channels against the `chan.min_capacity` criterium. Only

If a channel matches this policy, the `static` strategy is then used, which takes the `base_fee_msat` and `fee_ppm` properties defined in the policy and applies them to the channel.

If at least lnd 0.18 is used, charge-lnd also supports the experimental support of inbound fees. By default, lnd only supports negative inbound fees on the inbound channel, which then act as a “discount” on the outbound fees of the outgoing channel. However, the entire forward fee cannot become negative.

Example with inbound fees:
```
[example-policy]
chan.min_capacity = 500000
strategy = static
base_fee_msat = 1000
fee_ppm = 2000
inbound_base_fee_msat = -500
inbound_fee_ppm = -1000
```

### Non-final policies

You can also define a 'non-final' policy. This is simply a policy without a strategy.
Expand Down Expand Up @@ -102,6 +116,8 @@ chan.min_capacity = 250000
strategy = static
base_fee_msat = 10000
fee_ppm = 500
inbound_base_fee_msat = -8000
inbound_fee_ppm = -400
[encourage-routing-to-balance]
chan.min_ratio = 0.9
Expand Down Expand Up @@ -187,11 +203,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.| **fee_ppm**|
|**match_peer** | sets the same base fee and fee rate values as the peer|if **base_fee_msat** or **fee_ppm** are set the override the peer values|
|**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**|
|**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 (Requires --electrum-server to be specified.)| **onchain_fee_btc** BTC<br>within **onchain_fee_numblocks** blocks.|
|**proportional** | sets fee ppm according to balancedness.|**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 fee ppm keeps unchanged.|**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**|

Expand Down
39 changes: 28 additions & 11 deletions charge_lnd/charge_lnd.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def main():
if not policy:
continue

(new_base_fee_msat, new_fee_ppm, new_min_htlc, new_max_htlc, new_time_lock_delta, disable) = policy.strategy.execute(channel)
(new_base_fee_msat, new_fee_ppm, new_inbound_base_fee_msat, new_inbound_fee_ppm, new_min_htlc, new_max_htlc, new_time_lock_delta, disable) = policy.strategy.execute(channel)

if channel.chan_id in lnd.feereport:
(current_base_fee_msat, current_fee_ppm) = lnd.feereport[channel.chan_id]
Expand All @@ -65,12 +65,16 @@ def main():
min_fee_ppm_delta = policy.getint('min_fee_ppm_delta',0)

fee_ppm_changed = new_fee_ppm is not None and current_fee_ppm != new_fee_ppm and abs(current_fee_ppm - new_fee_ppm) >= min_fee_ppm_delta
inbound_fee_ppm_changed = new_inbound_fee_ppm is not None and my_policy.inbound_fee_rate_milli_msat != new_inbound_fee_ppm and \
abs(my_policy.inbound_fee_rate_milli_msat - new_inbound_fee_ppm) >= min_fee_ppm_delta

base_fee_changed = new_base_fee_msat is not None and current_base_fee_msat != new_base_fee_msat
inbound_base_fee_changed = new_inbound_base_fee_msat is not None and my_policy.inbound_fee_base_msat != new_inbound_base_fee_msat
min_htlc_changed = new_min_htlc is not None and my_policy.min_htlc != new_min_htlc
max_htlc_changed = new_max_htlc is not None and my_policy.max_htlc_msat != new_max_htlc
time_lock_delta_changed = new_time_lock_delta is not None and my_policy.time_lock_delta != new_time_lock_delta
is_changed = fee_ppm_changed or base_fee_changed or min_htlc_changed or max_htlc_changed or time_lock_delta_changed
is_changed = fee_ppm_changed or base_fee_changed or min_htlc_changed or max_htlc_changed or \
time_lock_delta_changed or inbound_base_fee_changed + inbound_fee_ppm_changed

chan_status_changed = False
if lnd.min_version(0,13) and channel.active and disable != my_policy.disabled and policy.get('strategy') != 'ignore':
Expand All @@ -85,44 +89,57 @@ def main():
)

if is_changed and not arguments.dry_run:
lnd.update_chan_policy(channel.chan_id, new_base_fee_msat, new_fee_ppm, new_min_htlc, new_max_htlc, new_time_lock_delta)
lnd.update_chan_policy(channel.chan_id, new_base_fee_msat, new_fee_ppm, new_min_htlc,
new_max_htlc, new_time_lock_delta, new_inbound_base_fee_msat, new_inbound_fee_ppm)

if is_changed or chan_status_changed or arguments.verbose:
print(" policy: %s" % fmt.col_hi(policy.name) )
print(" strategy: %s" % fmt.col_hi(policy.get('strategy')) )
print(" policy: %s" % fmt.col_hi(policy.name) )
print(" strategy: %s" % fmt.col_hi(policy.get('strategy')) )
if chan_status_changed or arguments.verbose:
s = 'disabled' if my_policy.disabled else 'enabled'
if chan_status_changed:
s = s + ' ➜ '
s = s + 'disabled' if disable else 'enabled'
print(" channel status: %s" % fmt.col_hi(s))
print(" channel status: %s" % fmt.col_hi(s))
if new_base_fee_msat is not None or arguments.verbose:
s = ''
if base_fee_changed:
s = ' ➜ ' + fmt.col_hi(new_base_fee_msat)
print(" base_fee_msat: %s%s" % (fmt.col_hi(current_base_fee_msat), s) )
print(" base_fee_msat: %s%s" % (fmt.col_hi(current_base_fee_msat), s) )
if new_fee_ppm is not None or arguments.verbose:
s = ''
if fee_ppm_changed:
s = ' ➜ ' + fmt.col_hi(new_fee_ppm)
if min_fee_ppm_delta > abs(new_fee_ppm - current_fee_ppm):
s = s + ' (min_fee_ppm_delta=%d)' % min_fee_ppm_delta
print(" fee_ppm: %s%s" % (fmt.col_hi(current_fee_ppm), s) )
print(" fee_ppm: %s%s" % (fmt.col_hi(current_fee_ppm), s) )
if new_inbound_base_fee_msat is not None or arguments.verbose:
s = ''
if inbound_base_fee_changed:
s = ' ➜ ' + fmt.col_hi(new_inbound_base_fee_msat)
print(" inbound_base_fee_msat: %s%s" % (fmt.col_hi(my_policy.inbound_fee_base_msat), s) )
if new_inbound_fee_ppm is not None or arguments.verbose:
s = ''
if inbound_fee_ppm_changed:
s = ' ➜ ' + fmt.col_hi(new_inbound_fee_ppm)
if min_fee_ppm_delta > abs(new_inbound_fee_ppm - my_policy.inbound_fee_rate_milli_msat):
s = s + ' (min_fee_ppm_delta=%d)' % min_fee_ppm_delta
print(" inbound_fee_ppm: %s%s" % (fmt.col_hi(my_policy.inbound_fee_rate_milli_msat), s) )
if new_min_htlc is not None or arguments.verbose:
s = ''
if min_htlc_changed:
s = ' ➜ ' + fmt.col_hi(new_min_htlc)
print(" min_htlc_msat: %s%s" % (fmt.col_hi(my_policy.min_htlc), s) )
print(" min_htlc_msat: %s%s" % (fmt.col_hi(my_policy.min_htlc), s) )
if new_max_htlc is not None or arguments.verbose:
s = ''
if max_htlc_changed:
s = ' ➜ ' + fmt.col_hi(new_max_htlc)
print(" max_htlc_msat: %s%s" % (fmt.col_hi(my_policy.max_htlc_msat), s) )
print(" max_htlc_msat: %s%s" % (fmt.col_hi(my_policy.max_htlc_msat), s) )
if new_time_lock_delta is not None or arguments.verbose:
s = ''
if time_lock_delta_changed:
s = ' ➜ ' + fmt.col_hi(new_time_lock_delta)
print(" time_lock_delta: %s%s" % (fmt.col_hi(my_policy.time_lock_delta), s) )
print(" time_lock_delta: %s%s" % (fmt.col_hi(my_policy.time_lock_delta), s) )

return True

Expand Down
671 changes: 671 additions & 0 deletions charge_lnd/grpc_generated/lightning_pb2.py

Large diffs are not rendered by default.

Loading

0 comments on commit 14cb8a0

Please sign in to comment.