Merge pull request #110 from feelancer21/inbound_fees

Inbound fees
This commit is contained in:
accumulator 2024-05-15 17:05:22 +02:00 committed by GitHub
commit 14cb8a0aa3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 4656 additions and 17997 deletions

View file

@ -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.
@ -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
@ -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**|

View file

@ -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]
@ -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':
@ -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

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

@ -1,13 +1,55 @@
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
"""Client and server classes corresponding to protobuf-defined services."""
import grpc
import warnings
from . import lightning_pb2 as lightning__pb2
from . import router_pb2 as router__pb2
from . import rpc_pb2 as rpc__pb2
GRPC_GENERATED_VERSION = '1.63.0'
GRPC_VERSION = grpc.__version__
EXPECTED_ERROR_RELEASE = '1.65.0'
SCHEDULED_RELEASE_DATE = 'June 25, 2024'
_version_not_supported = False
try:
from grpc._utilities import first_version_is_lower
_version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION)
except ImportError:
_version_not_supported = True
if _version_not_supported:
warnings.warn(
f'The grpc package installed is at version {GRPC_VERSION},'
+ f' but the generated code in router_pb2_grpc.py depends on'
+ f' grpcio>={GRPC_GENERATED_VERSION}.'
+ f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}'
+ f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.'
+ f' This warning will become an error in {EXPECTED_ERROR_RELEASE},'
+ f' scheduled for release on {SCHEDULED_RELEASE_DATE}.',
RuntimeWarning
)
class RouterStub(object):
"""Router is a service that offers advanced interaction with the router
"""
Comments in this file will be directly parsed into the API
Documentation as descriptions of the associated method, message, or field.
These descriptions should go right above the definition of the object, and
can be in either block or // comment format.
An RPC method can be matched to an lncli command by placing a line in the
beginning of the description in exactly the following format:
lncli: `methodname`
Failure to specify the exact name of the command will cause documentation
generation to fail.
More information on how exactly the gRPC documentation is generated from
this proto file can be found here:
https://github.com/lightninglabs/lightning-api
Router is a service that offers advanced interaction with the router
subsystem of the daemon.
"""
@ -20,92 +62,114 @@ class RouterStub(object):
self.SendPaymentV2 = channel.unary_stream(
'/routerrpc.Router/SendPaymentV2',
request_serializer=router__pb2.SendPaymentRequest.SerializeToString,
response_deserializer=rpc__pb2.Payment.FromString,
)
response_deserializer=lightning__pb2.Payment.FromString,
_registered_method=True)
self.TrackPaymentV2 = channel.unary_stream(
'/routerrpc.Router/TrackPaymentV2',
request_serializer=router__pb2.TrackPaymentRequest.SerializeToString,
response_deserializer=rpc__pb2.Payment.FromString,
)
response_deserializer=lightning__pb2.Payment.FromString,
_registered_method=True)
self.TrackPayments = channel.unary_stream(
'/routerrpc.Router/TrackPayments',
request_serializer=router__pb2.TrackPaymentsRequest.SerializeToString,
response_deserializer=lightning__pb2.Payment.FromString,
_registered_method=True)
self.EstimateRouteFee = channel.unary_unary(
'/routerrpc.Router/EstimateRouteFee',
request_serializer=router__pb2.RouteFeeRequest.SerializeToString,
response_deserializer=router__pb2.RouteFeeResponse.FromString,
)
_registered_method=True)
self.SendToRoute = channel.unary_unary(
'/routerrpc.Router/SendToRoute',
request_serializer=router__pb2.SendToRouteRequest.SerializeToString,
response_deserializer=router__pb2.SendToRouteResponse.FromString,
)
_registered_method=True)
self.SendToRouteV2 = channel.unary_unary(
'/routerrpc.Router/SendToRouteV2',
request_serializer=router__pb2.SendToRouteRequest.SerializeToString,
response_deserializer=rpc__pb2.HTLCAttempt.FromString,
)
response_deserializer=lightning__pb2.HTLCAttempt.FromString,
_registered_method=True)
self.ResetMissionControl = channel.unary_unary(
'/routerrpc.Router/ResetMissionControl',
request_serializer=router__pb2.ResetMissionControlRequest.SerializeToString,
response_deserializer=router__pb2.ResetMissionControlResponse.FromString,
)
_registered_method=True)
self.QueryMissionControl = channel.unary_unary(
'/routerrpc.Router/QueryMissionControl',
request_serializer=router__pb2.QueryMissionControlRequest.SerializeToString,
response_deserializer=router__pb2.QueryMissionControlResponse.FromString,
)
_registered_method=True)
self.XImportMissionControl = channel.unary_unary(
'/routerrpc.Router/XImportMissionControl',
request_serializer=router__pb2.XImportMissionControlRequest.SerializeToString,
response_deserializer=router__pb2.XImportMissionControlResponse.FromString,
)
_registered_method=True)
self.GetMissionControlConfig = channel.unary_unary(
'/routerrpc.Router/GetMissionControlConfig',
request_serializer=router__pb2.GetMissionControlConfigRequest.SerializeToString,
response_deserializer=router__pb2.GetMissionControlConfigResponse.FromString,
)
_registered_method=True)
self.SetMissionControlConfig = channel.unary_unary(
'/routerrpc.Router/SetMissionControlConfig',
request_serializer=router__pb2.SetMissionControlConfigRequest.SerializeToString,
response_deserializer=router__pb2.SetMissionControlConfigResponse.FromString,
)
_registered_method=True)
self.QueryProbability = channel.unary_unary(
'/routerrpc.Router/QueryProbability',
request_serializer=router__pb2.QueryProbabilityRequest.SerializeToString,
response_deserializer=router__pb2.QueryProbabilityResponse.FromString,
)
_registered_method=True)
self.BuildRoute = channel.unary_unary(
'/routerrpc.Router/BuildRoute',
request_serializer=router__pb2.BuildRouteRequest.SerializeToString,
response_deserializer=router__pb2.BuildRouteResponse.FromString,
)
_registered_method=True)
self.SubscribeHtlcEvents = channel.unary_stream(
'/routerrpc.Router/SubscribeHtlcEvents',
request_serializer=router__pb2.SubscribeHtlcEventsRequest.SerializeToString,
response_deserializer=router__pb2.HtlcEvent.FromString,
)
_registered_method=True)
self.SendPayment = channel.unary_stream(
'/routerrpc.Router/SendPayment',
request_serializer=router__pb2.SendPaymentRequest.SerializeToString,
response_deserializer=router__pb2.PaymentStatus.FromString,
)
_registered_method=True)
self.TrackPayment = channel.unary_stream(
'/routerrpc.Router/TrackPayment',
request_serializer=router__pb2.TrackPaymentRequest.SerializeToString,
response_deserializer=router__pb2.PaymentStatus.FromString,
)
_registered_method=True)
self.HtlcInterceptor = channel.stream_stream(
'/routerrpc.Router/HtlcInterceptor',
request_serializer=router__pb2.ForwardHtlcInterceptResponse.SerializeToString,
response_deserializer=router__pb2.ForwardHtlcInterceptRequest.FromString,
)
_registered_method=True)
self.UpdateChanStatus = channel.unary_unary(
'/routerrpc.Router/UpdateChanStatus',
request_serializer=router__pb2.UpdateChanStatusRequest.SerializeToString,
response_deserializer=router__pb2.UpdateChanStatusResponse.FromString,
)
_registered_method=True)
class RouterServicer(object):
"""Router is a service that offers advanced interaction with the router
"""
Comments in this file will be directly parsed into the API
Documentation as descriptions of the associated method, message, or field.
These descriptions should go right above the definition of the object, and
can be in either block or // comment format.
An RPC method can be matched to an lncli command by placing a line in the
beginning of the description in exactly the following format:
lncli: `methodname`
Failure to specify the exact name of the command will cause documentation
generation to fail.
More information on how exactly the gRPC documentation is generated from
this proto file can be found here:
https://github.com/lightninglabs/lightning-api
Router is a service that offers advanced interaction with the router
subsystem of the daemon.
"""
@ -113,14 +177,17 @@ class RouterServicer(object):
"""
SendPaymentV2 attempts to route a payment described by the passed
PaymentRequest to the final destination. The call returns a stream of
payment updates.
payment updates. When using this RPC, make sure to set a fee limit, as the
default routing fee limit is 0 sats. Without a non-zero fee limit only
routes without fees will be attempted which often fails with
FAILURE_REASON_NO_ROUTE.
"""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def TrackPaymentV2(self, request, context):
"""
"""lncli: `trackpayment`
TrackPaymentV2 returns an update stream for the payment identified by the
payment hash.
"""
@ -128,6 +195,19 @@ class RouterServicer(object):
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def TrackPayments(self, request, context):
"""
TrackPayments returns an update stream for every payment that is not in a
terminal state. Note that if payments are in-flight while starting a new
subscription, the start of the payment stream could produce out-of-order
and/or duplicate events. In order to get updates for every in-flight
payment attempt make sure to subscribe to this method before initiating any
payments.
"""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def EstimateRouteFee(self, request, context):
"""
EstimateRouteFee allows callers to obtain a lower bound w.r.t how much it
@ -161,7 +241,7 @@ class RouterServicer(object):
raise NotImplementedError('Method not implemented!')
def ResetMissionControl(self, request, context):
"""
"""lncli: `resetmc`
ResetMissionControl clears all mission control state and starts with a clean
slate.
"""
@ -170,7 +250,7 @@ class RouterServicer(object):
raise NotImplementedError('Method not implemented!')
def QueryMissionControl(self, request, context):
"""
"""lncli: `querymc`
QueryMissionControl exposes the internal mission control state to callers.
It is a development feature.
"""
@ -179,7 +259,7 @@ class RouterServicer(object):
raise NotImplementedError('Method not implemented!')
def XImportMissionControl(self, request, context):
"""
"""lncli: `importmc`
XImportMissionControl is an experimental API that imports the state provided
to the internal mission control's state, using all results which are more
recent than our existing values. These values will only be imported
@ -190,7 +270,7 @@ class RouterServicer(object):
raise NotImplementedError('Method not implemented!')
def GetMissionControlConfig(self, request, context):
"""
"""lncli: `getmccfg`
GetMissionControlConfig returns mission control's current config.
"""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
@ -198,7 +278,7 @@ class RouterServicer(object):
raise NotImplementedError('Method not implemented!')
def SetMissionControlConfig(self, request, context):
"""
"""lncli: `setmccfg`
SetMissionControlConfig will set mission control's config, if the config
provided is valid.
"""
@ -207,19 +287,25 @@ class RouterServicer(object):
raise NotImplementedError('Method not implemented!')
def QueryProbability(self, request, context):
"""
QueryProbability returns the current success probability estimate for a
given node pair and amount.
"""lncli: `queryprob`
Deprecated. QueryProbability returns the current success probability
estimate for a given node pair and amount. The call returns a zero success
probability if no channel is available or if the amount violates min/max
HTLC constraints.
"""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def BuildRoute(self, request, context):
"""
"""lncli: `buildroute`
BuildRoute builds a fully specified route based on a list of hop public
keys. It retrieves the relevant channel policies from the graph in order to
calculate the correct fees and time locks.
Note that LND will use its default final_cltv_delta if no value is supplied.
Make sure to add the correct final_cltv_delta depending on the invoice
restriction. Moreover the caller has to make sure to provide the
payment_addr if the route is paying an invoice which signaled it.
"""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
@ -266,7 +352,7 @@ class RouterServicer(object):
raise NotImplementedError('Method not implemented!')
def UpdateChanStatus(self, request, context):
"""
"""lncli: `updatechanstatus`
UpdateChanStatus attempts to manually set the state of a channel
(enabled, disabled, or auto). A manual "disable" request will cause the
channel to stay disabled until a subsequent manual request of either
@ -282,12 +368,17 @@ def add_RouterServicer_to_server(servicer, server):
'SendPaymentV2': grpc.unary_stream_rpc_method_handler(
servicer.SendPaymentV2,
request_deserializer=router__pb2.SendPaymentRequest.FromString,
response_serializer=rpc__pb2.Payment.SerializeToString,
response_serializer=lightning__pb2.Payment.SerializeToString,
),
'TrackPaymentV2': grpc.unary_stream_rpc_method_handler(
servicer.TrackPaymentV2,
request_deserializer=router__pb2.TrackPaymentRequest.FromString,
response_serializer=rpc__pb2.Payment.SerializeToString,
response_serializer=lightning__pb2.Payment.SerializeToString,
),
'TrackPayments': grpc.unary_stream_rpc_method_handler(
servicer.TrackPayments,
request_deserializer=router__pb2.TrackPaymentsRequest.FromString,
response_serializer=lightning__pb2.Payment.SerializeToString,
),
'EstimateRouteFee': grpc.unary_unary_rpc_method_handler(
servicer.EstimateRouteFee,
@ -302,7 +393,7 @@ def add_RouterServicer_to_server(servicer, server):
'SendToRouteV2': grpc.unary_unary_rpc_method_handler(
servicer.SendToRouteV2,
request_deserializer=router__pb2.SendToRouteRequest.FromString,
response_serializer=rpc__pb2.HTLCAttempt.SerializeToString,
response_serializer=lightning__pb2.HTLCAttempt.SerializeToString,
),
'ResetMissionControl': grpc.unary_unary_rpc_method_handler(
servicer.ResetMissionControl,
@ -372,7 +463,24 @@ def add_RouterServicer_to_server(servicer, server):
# This class is part of an EXPERIMENTAL API.
class Router(object):
"""Router is a service that offers advanced interaction with the router
"""
Comments in this file will be directly parsed into the API
Documentation as descriptions of the associated method, message, or field.
These descriptions should go right above the definition of the object, and
can be in either block or // comment format.
An RPC method can be matched to an lncli command by placing a line in the
beginning of the description in exactly the following format:
lncli: `methodname`
Failure to specify the exact name of the command will cause documentation
generation to fail.
More information on how exactly the gRPC documentation is generated from
this proto file can be found here:
https://github.com/lightninglabs/lightning-api
Router is a service that offers advanced interaction with the router
subsystem of the daemon.
"""
@ -387,11 +495,21 @@ class Router(object):
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_stream(request, target, '/routerrpc.Router/SendPaymentV2',
return grpc.experimental.unary_stream(
request,
target,
'/routerrpc.Router/SendPaymentV2',
router__pb2.SendPaymentRequest.SerializeToString,
rpc__pb2.Payment.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
lightning__pb2.Payment.FromString,
options,
channel_credentials,
insecure,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
_registered_method=True)
@staticmethod
def TrackPaymentV2(request,
@ -404,11 +522,48 @@ class Router(object):
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_stream(request, target, '/routerrpc.Router/TrackPaymentV2',
return grpc.experimental.unary_stream(
request,
target,
'/routerrpc.Router/TrackPaymentV2',
router__pb2.TrackPaymentRequest.SerializeToString,
rpc__pb2.Payment.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
lightning__pb2.Payment.FromString,
options,
channel_credentials,
insecure,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
_registered_method=True)
@staticmethod
def TrackPayments(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_stream(
request,
target,
'/routerrpc.Router/TrackPayments',
router__pb2.TrackPaymentsRequest.SerializeToString,
lightning__pb2.Payment.FromString,
options,
channel_credentials,
insecure,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
_registered_method=True)
@staticmethod
def EstimateRouteFee(request,
@ -421,11 +576,21 @@ class Router(object):
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/routerrpc.Router/EstimateRouteFee',
return grpc.experimental.unary_unary(
request,
target,
'/routerrpc.Router/EstimateRouteFee',
router__pb2.RouteFeeRequest.SerializeToString,
router__pb2.RouteFeeResponse.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
options,
channel_credentials,
insecure,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
_registered_method=True)
@staticmethod
def SendToRoute(request,
@ -438,11 +603,21 @@ class Router(object):
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/routerrpc.Router/SendToRoute',
return grpc.experimental.unary_unary(
request,
target,
'/routerrpc.Router/SendToRoute',
router__pb2.SendToRouteRequest.SerializeToString,
router__pb2.SendToRouteResponse.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
options,
channel_credentials,
insecure,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
_registered_method=True)
@staticmethod
def SendToRouteV2(request,
@ -455,11 +630,21 @@ class Router(object):
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/routerrpc.Router/SendToRouteV2',
return grpc.experimental.unary_unary(
request,
target,
'/routerrpc.Router/SendToRouteV2',
router__pb2.SendToRouteRequest.SerializeToString,
rpc__pb2.HTLCAttempt.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
lightning__pb2.HTLCAttempt.FromString,
options,
channel_credentials,
insecure,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
_registered_method=True)
@staticmethod
def ResetMissionControl(request,
@ -472,11 +657,21 @@ class Router(object):
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/routerrpc.Router/ResetMissionControl',
return grpc.experimental.unary_unary(
request,
target,
'/routerrpc.Router/ResetMissionControl',
router__pb2.ResetMissionControlRequest.SerializeToString,
router__pb2.ResetMissionControlResponse.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
options,
channel_credentials,
insecure,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
_registered_method=True)
@staticmethod
def QueryMissionControl(request,
@ -489,11 +684,21 @@ class Router(object):
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/routerrpc.Router/QueryMissionControl',
return grpc.experimental.unary_unary(
request,
target,
'/routerrpc.Router/QueryMissionControl',
router__pb2.QueryMissionControlRequest.SerializeToString,
router__pb2.QueryMissionControlResponse.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
options,
channel_credentials,
insecure,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
_registered_method=True)
@staticmethod
def XImportMissionControl(request,
@ -506,11 +711,21 @@ class Router(object):
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/routerrpc.Router/XImportMissionControl',
return grpc.experimental.unary_unary(
request,
target,
'/routerrpc.Router/XImportMissionControl',
router__pb2.XImportMissionControlRequest.SerializeToString,
router__pb2.XImportMissionControlResponse.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
options,
channel_credentials,
insecure,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
_registered_method=True)
@staticmethod
def GetMissionControlConfig(request,
@ -523,11 +738,21 @@ class Router(object):
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/routerrpc.Router/GetMissionControlConfig',
return grpc.experimental.unary_unary(
request,
target,
'/routerrpc.Router/GetMissionControlConfig',
router__pb2.GetMissionControlConfigRequest.SerializeToString,
router__pb2.GetMissionControlConfigResponse.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
options,
channel_credentials,
insecure,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
_registered_method=True)
@staticmethod
def SetMissionControlConfig(request,
@ -540,11 +765,21 @@ class Router(object):
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/routerrpc.Router/SetMissionControlConfig',
return grpc.experimental.unary_unary(
request,
target,
'/routerrpc.Router/SetMissionControlConfig',
router__pb2.SetMissionControlConfigRequest.SerializeToString,
router__pb2.SetMissionControlConfigResponse.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
options,
channel_credentials,
insecure,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
_registered_method=True)
@staticmethod
def QueryProbability(request,
@ -557,11 +792,21 @@ class Router(object):
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/routerrpc.Router/QueryProbability',
return grpc.experimental.unary_unary(
request,
target,
'/routerrpc.Router/QueryProbability',
router__pb2.QueryProbabilityRequest.SerializeToString,
router__pb2.QueryProbabilityResponse.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
options,
channel_credentials,
insecure,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
_registered_method=True)
@staticmethod
def BuildRoute(request,
@ -574,11 +819,21 @@ class Router(object):
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/routerrpc.Router/BuildRoute',
return grpc.experimental.unary_unary(
request,
target,
'/routerrpc.Router/BuildRoute',
router__pb2.BuildRouteRequest.SerializeToString,
router__pb2.BuildRouteResponse.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
options,
channel_credentials,
insecure,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
_registered_method=True)
@staticmethod
def SubscribeHtlcEvents(request,
@ -591,11 +846,21 @@ class Router(object):
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_stream(request, target, '/routerrpc.Router/SubscribeHtlcEvents',
return grpc.experimental.unary_stream(
request,
target,
'/routerrpc.Router/SubscribeHtlcEvents',
router__pb2.SubscribeHtlcEventsRequest.SerializeToString,
router__pb2.HtlcEvent.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
options,
channel_credentials,
insecure,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
_registered_method=True)
@staticmethod
def SendPayment(request,
@ -608,11 +873,21 @@ class Router(object):
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_stream(request, target, '/routerrpc.Router/SendPayment',
return grpc.experimental.unary_stream(
request,
target,
'/routerrpc.Router/SendPayment',
router__pb2.SendPaymentRequest.SerializeToString,
router__pb2.PaymentStatus.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
options,
channel_credentials,
insecure,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
_registered_method=True)
@staticmethod
def TrackPayment(request,
@ -625,11 +900,21 @@ class Router(object):
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_stream(request, target, '/routerrpc.Router/TrackPayment',
return grpc.experimental.unary_stream(
request,
target,
'/routerrpc.Router/TrackPayment',
router__pb2.TrackPaymentRequest.SerializeToString,
router__pb2.PaymentStatus.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
options,
channel_credentials,
insecure,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
_registered_method=True)
@staticmethod
def HtlcInterceptor(request_iterator,
@ -642,11 +927,21 @@ class Router(object):
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.stream_stream(request_iterator, target, '/routerrpc.Router/HtlcInterceptor',
return grpc.experimental.stream_stream(
request_iterator,
target,
'/routerrpc.Router/HtlcInterceptor',
router__pb2.ForwardHtlcInterceptResponse.SerializeToString,
router__pb2.ForwardHtlcInterceptRequest.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
options,
channel_credentials,
insecure,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
_registered_method=True)
@staticmethod
def UpdateChanStatus(request,
@ -659,8 +954,18 @@ class Router(object):
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/routerrpc.Router/UpdateChanStatus',
return grpc.experimental.unary_unary(
request,
target,
'/routerrpc.Router/UpdateChanStatus',
router__pb2.UpdateChanStatusRequest.SerializeToString,
router__pb2.UpdateChanStatusResponse.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
options,
channel_credentials,
insecure,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
_registered_method=True)

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

View file

@ -7,7 +7,7 @@ import sys
import re
import time
from .grpc_generated import rpc_pb2_grpc as lnrpc, rpc_pb2 as ln
from .grpc_generated import lightning_pb2_grpc as lnrpc, lightning_pb2 as ln
from .grpc_generated import router_pb2_grpc as routerrpc, router_pb2 as router
MESSAGE_SIZE_MB = 50 * 1024 * 1024
@ -140,7 +140,8 @@ class Lnd:
return None
return self.chan_info[chanid]
def update_chan_policy(self, chanid, base_fee_msat, fee_ppm, min_htlc_msat, max_htlc_msat, time_lock_delta):
def update_chan_policy(self, chanid, base_fee_msat, fee_ppm, min_htlc_msat, max_htlc_msat,
time_lock_delta, inbound_base_fee_msat, inbound_fee_ppm):
chan_info = self.get_chan_info(chanid)
if not chan_info:
return None
@ -156,7 +157,9 @@ class Lnd:
min_htlc_msat=(min_htlc_msat if min_htlc_msat is not None else my_policy.min_htlc),
min_htlc_msat_specified=min_htlc_msat is not None,
max_htlc_msat=(max_htlc_msat if max_htlc_msat is not None else my_policy.max_htlc_msat),
time_lock_delta=(time_lock_delta if time_lock_delta is not None else my_policy.time_lock_delta)
time_lock_delta=(time_lock_delta if time_lock_delta is not None else my_policy.time_lock_delta),
inbound_base_fee_msat=(inbound_base_fee_msat if inbound_base_fee_msat is not None else my_policy.inbound_fee_base_msat),
inbound_fee_rate_ppm=(inbound_fee_ppm if inbound_fee_ppm is not None else my_policy.inbound_fee_rate_milli_msat)
))
def get_txns(self, start_height = None, end_height = None):

View file

@ -33,12 +33,12 @@ class StrategyDelegate:
try:
result = StrategyDelegate.STRATEGIES[strategy](channel, self.policy, name=self.policy.name, lnd=self.policy.lnd)
# set policy htlc limits if not overruled by the strategy
if len(result) == 2:
if len(result) == 4:
result = result + ( self.policy.getint('min_htlc_msat'),
self.effective_max_htlc_msat(channel),
self.policy.getint('time_lock_delta') )
# disabled = False by default
if len(result) == 5:
if len(result) == 7:
result = result + ( False, )
return result
@ -62,15 +62,16 @@ class StrategyDelegate:
@strategy(name = 'ignore')
def strategy_ignore(channel, policy, **kwargs):
return (None, None, None, None, None)
return (None, None, None, None, None, None, None)
@strategy(name = 'ignore_fees')
def strategy_ignore_fees(channel, policy, **kwargs):
return (None, None)
return (None, None, None, None)
@strategy(name = 'static')
def strategy_static(channel, policy, **kwargs):
return (policy.getint('base_fee_msat'), policy.getint('fee_ppm'))
return (policy.getint('base_fee_msat'), policy.getint('fee_ppm'),
policy.getint('inbound_base_fee_msat'), policy.getint('inbound_fee_ppm'))
@strategy(name = 'proportional')
def strategy_proportional(channel, policy, **kwargs):
@ -107,7 +108,7 @@ def strategy_proportional(channel, policy, **kwargs):
ppm = int(ppm_min + (1.0 - ratio) * (ppm_max - ppm_min))
# clamp to 0..inf
ppm = max(ppm,0)
return (policy.getint('base_fee_msat'), ppm)
return (policy.getint('base_fee_msat'), ppm, None, None)
@strategy(name = 'match_peer')
def strategy_match_peer(channel, policy, **kwargs):
@ -116,7 +117,9 @@ def strategy_match_peer(channel, policy, **kwargs):
my_pubkey = lnd.get_own_pubkey()
peernode_policy = chan_info.node1_policy if chan_info.node2_pub == my_pubkey else chan_info.node2_policy
return (policy.getint('base_fee_msat', peernode_policy.fee_base_msat),
policy.getint('fee_ppm', peernode_policy.fee_rate_milli_msat))
policy.getint('fee_ppm', peernode_policy.fee_rate_milli_msat),
policy.getint('inbound_base_fee_msat', peernode_policy.inbound_fee_base_msat),
policy.getint('inbound_fee_ppm', peernode_policy.inbound_fee_rate_milli_msat))
@strategy(name = 'cost')
def strategy_cost(channel, policy, **kwargs):
@ -135,7 +138,7 @@ def strategy_cost(channel, policy, **kwargs):
ppm = int(policy.getfloat('cost_factor', 1.0) * 1_000_000 * chan_open_tx.total_fees / chan_info.capacity)
else:
ppm = 1 # tx not found, incoming channel, default to 1
return (policy.getint('base_fee_msat'), ppm)
return (policy.getint('base_fee_msat'), ppm, None, None)
@strategy(name = 'onchain_fee')
def strategy_onchain_fee(channel, policy, **kwargs):
@ -151,7 +154,7 @@ def strategy_onchain_fee(channel, policy, **kwargs):
return (None, None, None, None, None)
reference_payment = policy.getfloat('onchain_fee_btc', 0.1)
fee_ppm = int((0.01 / reference_payment) * (223 * sat_per_byte))
return (policy.getint('base_fee_msat'), fee_ppm)
return (policy.getint('base_fee_msat'), fee_ppm, None, None)
@strategy(name = 'use_config')
def strategy_use_config(channel, policy, **kwargs):

View file

@ -4,3 +4,5 @@
strategy = static
base_fee_msat = 1000
fee_ppm = 200
inbound_base_fee_msat = -500
inbound_fee_ppm = -100

View file

@ -3,6 +3,8 @@
strategy = static
base_fee_msat = 1_000
fee_ppm = 10
inbound_base_fee_msat = -500
inbound_fee_ppm = -5
[mydefaults]
# no strategy, so this only sets some defaults

View file

@ -1,7 +1,7 @@
setuptools
googleapis-common-protos==1.56.0
grpcio==1.53.2
protobuf==3.20.2
googleapis-common-protos==1.56.1
grpcio==1.63.0
protobuf==5.26.1
six==1.16.0
termcolor==1.1.0
colorama==0.4.4