inbound fees (#391)

This commit is contained in:
cryptosharks131 2024-06-22 22:41:29 -04:00 committed by GitHub
parent 9c8838b8d8
commit b1f1419e45
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 872 additions and 2280 deletions

View file

@ -84,6 +84,8 @@ updates_channel_codes = [
(9, 'cltv'),
(10, 'min_htlc'),
(11, 'max_htlc'),
(12, 'inbound_base_fee'),
(13, 'inbound_fee_rate'),
]
class UpdateChannel(forms.Form):

File diff suppressed because one or more lines are too long

View file

@ -112,6 +112,11 @@ class LightningStub(object):
request_serializer=lightning__pb2.GetInfoRequest.SerializeToString,
response_deserializer=lightning__pb2.GetInfoResponse.FromString,
)
self.GetDebugInfo = channel.unary_unary(
'/lnrpc.Lightning/GetDebugInfo',
request_serializer=lightning__pb2.GetDebugInfoRequest.SerializeToString,
response_deserializer=lightning__pb2.GetDebugInfoResponse.FromString,
)
self.GetRecoveryInfo = channel.unary_unary(
'/lnrpc.Lightning/GetRecoveryInfo',
request_serializer=lightning__pb2.GetRecoveryInfoRequest.SerializeToString,
@ -357,6 +362,16 @@ class LightningStub(object):
request_serializer=lightning__pb2.SubscribeCustomMessagesRequest.SerializeToString,
response_deserializer=lightning__pb2.CustomMessage.FromString,
)
self.ListAliases = channel.unary_unary(
'/lnrpc.Lightning/ListAliases',
request_serializer=lightning__pb2.ListAliasesRequest.SerializeToString,
response_deserializer=lightning__pb2.ListAliasesResponse.FromString,
)
self.LookupHtlcResolution = channel.unary_unary(
'/lnrpc.Lightning/LookupHtlcResolution',
request_serializer=lightning__pb2.LookupHtlcResolutionRequest.SerializeToString,
response_deserializer=lightning__pb2.LookupHtlcResolutionResponse.FromString,
)
class LightningServicer(object):
@ -487,8 +502,10 @@ class LightningServicer(object):
def VerifyMessage(self, request, context):
"""lncli: `verifymessage`
VerifyMessage verifies a signature over a msg. The signature must be
zbase32 encoded and signed by an active node in the resident node's
VerifyMessage verifies a signature over a message and recovers the signer's
public key. The signature is only deemed valid if the recovered public key
corresponds to a node key in the public Lightning network. The signature
must be zbase32 encoded and signed by an active node in the resident node's
channel database. In addition to returning the validity of the signature,
VerifyMessage also returns the recovered pubkey from the signature.
"""
@ -544,6 +561,16 @@ class LightningServicer(object):
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def GetDebugInfo(self, request, context):
"""lncli: 'getdebuginfo'
GetDebugInfo returns debug information concerning the state of the daemon
and its subsystems. This includes the full configuration and the latest log
entries from the log file.
"""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def GetRecoveryInfo(self, request, context):
"""* lncli: `getrecoveryinfo`
GetRecoveryInfo returns information concerning the recovery mode including
@ -773,7 +800,7 @@ class LightningServicer(object):
optionally specify the add_index and/or the settle_index. If the add_index
is specified, then we'll first start by sending add invoice events for all
invoices with an add_index greater than the specified value. If the
settle_index is specified, the next, we'll send out all settle events for
settle_index is specified, then next, we'll send out all settle events for
invoices with a settle_index greater than the specified value. One or both
of these fields can be set. If no fields are set, then we'll only send out
the latest add/settle events.
@ -801,7 +828,7 @@ class LightningServicer(object):
raise NotImplementedError('Method not implemented!')
def DeletePayment(self, request, context):
"""
"""lncli: `deletepayments`
DeletePayment deletes an outgoing payment from DB. Note that it will not
attempt to delete an In-Flight payment, since that would be unsafe.
"""
@ -810,7 +837,7 @@ class LightningServicer(object):
raise NotImplementedError('Method not implemented!')
def DeleteAllPayments(self, request, context):
"""
"""lncli: `deletepayments --all`
DeleteAllPayments deletes all outgoing payments from DB. Note that it will
not attempt to delete In-Flight payments, since that would be unsafe.
"""
@ -981,7 +1008,7 @@ class LightningServicer(object):
raise NotImplementedError('Method not implemented!')
def VerifyChanBackup(self, request, context):
"""
"""lncli: `verifychanbackup`
VerifyChanBackup allows a caller to verify the integrity of a channel backup
snapshot. This method will accept either a packed Single or a packed Multi.
Specifying both will result in an error.
@ -1092,6 +1119,30 @@ class LightningServicer(object):
"""lncli: `subscribecustom`
SubscribeCustomMessages subscribes to a stream of incoming custom peer
messages.
To include messages with type outside of the custom range (>= 32768) lnd
needs to be compiled with the `dev` build tag, and the message type to
override should be specified in lnd's experimental protocol configuration.
"""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def ListAliases(self, request, context):
"""lncli: `listaliases`
ListAliases returns the set of all aliases that have ever existed with
their confirmed SCID (if it exists) and/or the base SCID (in the case of
zero conf).
"""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def LookupHtlcResolution(self, request, context):
"""
LookupHtlcResolution retrieves a final htlc resolution from the database.
If the htlc has no final resolution yet, a NotFound grpc status code is
returned.
"""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
@ -1180,6 +1231,11 @@ def add_LightningServicer_to_server(servicer, server):
request_deserializer=lightning__pb2.GetInfoRequest.FromString,
response_serializer=lightning__pb2.GetInfoResponse.SerializeToString,
),
'GetDebugInfo': grpc.unary_unary_rpc_method_handler(
servicer.GetDebugInfo,
request_deserializer=lightning__pb2.GetDebugInfoRequest.FromString,
response_serializer=lightning__pb2.GetDebugInfoResponse.SerializeToString,
),
'GetRecoveryInfo': grpc.unary_unary_rpc_method_handler(
servicer.GetRecoveryInfo,
request_deserializer=lightning__pb2.GetRecoveryInfoRequest.FromString,
@ -1425,6 +1481,16 @@ def add_LightningServicer_to_server(servicer, server):
request_deserializer=lightning__pb2.SubscribeCustomMessagesRequest.FromString,
response_serializer=lightning__pb2.CustomMessage.SerializeToString,
),
'ListAliases': grpc.unary_unary_rpc_method_handler(
servicer.ListAliases,
request_deserializer=lightning__pb2.ListAliasesRequest.FromString,
response_serializer=lightning__pb2.ListAliasesResponse.SerializeToString,
),
'LookupHtlcResolution': grpc.unary_unary_rpc_method_handler(
servicer.LookupHtlcResolution,
request_deserializer=lightning__pb2.LookupHtlcResolutionRequest.FromString,
response_serializer=lightning__pb2.LookupHtlcResolutionResponse.SerializeToString,
),
}
generic_handler = grpc.method_handlers_generic_handler(
'lnrpc.Lightning', rpc_method_handlers)
@ -1725,6 +1791,23 @@ class Lightning(object):
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def GetDebugInfo(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_unary(request, target, '/lnrpc.Lightning/GetDebugInfo',
lightning__pb2.GetDebugInfoRequest.SerializeToString,
lightning__pb2.GetDebugInfoResponse.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def GetRecoveryInfo(request,
target,
@ -2557,3 +2640,37 @@ class Lightning(object):
lightning__pb2.CustomMessage.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def ListAliases(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_unary(request, target, '/lnrpc.Lightning/ListAliases',
lightning__pb2.ListAliasesRequest.SerializeToString,
lightning__pb2.ListAliasesResponse.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def LookupHtlcResolution(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_unary(request, target, '/lnrpc.Lightning/LookupHtlcResolution',
lightning__pb2.LookupHtlcResolutionRequest.SerializeToString,
lightning__pb2.LookupHtlcResolutionResponse.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)

View file

@ -0,0 +1,43 @@
# Generated by Django 5.0.2 on 2024-05-08 22:27
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('gui', '0037_tradesales'),
]
operations = [
migrations.AddField(
model_name='channels',
name='local_inbound_base_fee',
field=models.IntegerField(default=0),
preserve_default=False,
),
migrations.AddField(
model_name='channels',
name='local_inbound_fee_rate',
field=models.IntegerField(default=0),
preserve_default=False,
),
migrations.AddField(
model_name='channels',
name='remote_inbound_base_fee',
field=models.IntegerField(default=0),
preserve_default=False,
),
migrations.AddField(
model_name='channels',
name='remote_inbound_fee_rate',
field=models.IntegerField(default=0),
preserve_default=False,
),
migrations.AddField(
model_name='forwards',
name='inbound_fee',
field=models.FloatField(default=0),
preserve_default=False,
),
]

View file

@ -60,6 +60,7 @@ class Forwards(models.Model):
amt_in_msat = models.BigIntegerField()
amt_out_msat = models.BigIntegerField()
fee = models.FloatField()
inbound_fee = models.FloatField()
class Meta:
app_label = 'gui'
@ -86,12 +87,16 @@ class Channels(models.Model):
htlc_count = models.IntegerField()
local_base_fee = models.IntegerField()
local_fee_rate = models.IntegerField()
local_inbound_base_fee = models.IntegerField()
local_inbound_fee_rate = models.IntegerField()
local_disabled = models.BooleanField()
local_cltv = models.IntegerField()
local_min_htlc_msat = models.BigIntegerField()
local_max_htlc_msat = models.BigIntegerField()
remote_base_fee = models.IntegerField()
remote_fee_rate = models.IntegerField()
remote_inbound_base_fee = models.IntegerField()
remote_inbound_fee_rate = models.IntegerField()
remote_disabled = models.BooleanField()
remote_cltv = models.IntegerField()
remote_min_htlc_msat = models.BigIntegerField()

View file

@ -155,6 +155,8 @@ class UpdateChanPolicy(serializers.Serializer):
chan_id = serializers.CharField(max_length=20)
base_fee = serializers.IntegerField(required=False, default=None)
fee_rate = serializers.IntegerField(required=False, default=None)
inbound_base_fee = serializers.IntegerField(required=False, default=None)
inbound_fee_rate = serializers.IntegerField(required=False, default=None)
disabled = serializers.IntegerField(required=False, default=None)
cltv = serializers.IntegerField(required=False, default=None)
min_htlc = serializers.FloatField(required=False, default=None)

View file

@ -23,6 +23,20 @@
<input type="hidden" name="key" value="ALL-oBase">
</form>
</td>
<td title="Update all channel inbound fee rates">
<form action="/update_setting/" method="post">
{% csrf_token %}
<input data-tag="inbound" style="text-align:center" id="value" type="number" min="-100000" max="0" name="value" value="">
<input type="hidden" name="key" value="ALL-iRate">
</form>
</td>
<td title="Update all channel inbound base fees">
<form action="/update_setting/" method="post">
{% csrf_token %}
<input data-tag="inbound" style="text-align:center" id="value" type="number" min="-100000000" max="0" name="value" value="">
<input type="hidden" name="key" value="ALL-iBase">
</form>
</td>
<td title="Update all channel CLTVs">
<form action="/update_setting/" method="post">
{% csrf_token %}
@ -37,7 +51,7 @@
<input type="hidden" name="key" value="ALL-minHTLC">
</form>
</td>
<th colspan="3"></th>
<th colspan="5"></th>
<td title="Update all channel AR amount amounts">
<form action="/update_setting/" method="post">
{% csrf_token %}
@ -83,17 +97,21 @@
<th>Channel State</th>
<th>oRate</th>
<th>oBase</th>
<th>iRate</th>
<th>iBase</th>
<th>oCLTV</th>
<th>minHTLC</th>
<th>maxHTLC</th>
<th onclick="sortTable(event.target, 11, 'int', 1)">iRate</th>
<th onclick="sortTable(event.target, 12, 'int', 1)">iBase</th>
<th onclick="sortTable(event.target, 13, 'int', 1)">Peer iRate</th>
<th onclick="sortTable(event.target, 14, 'int', 1)">Peer iBase</th>
<th onclick="sortTable(event.target, 15, 'int', 1)">Peer oRate</th>
<th onclick="sortTable(event.target, 16, 'int', 1)">Peer oBase</th>
<th title="When AR is ENABLED for the channel, the size of the rebalance attempts that should be tried during attempts to refill the channel.">Target Amt</th>
<th title="When AR is ENABLED, the maximum percentage amount of the local fee rate that can be used for the max rebalancing cost.">Max Cost %</th>
<th title="When AR is NOT ENABLED for the channel, keep pushing OUT the channel until its outbound liquidity falls below the oTarget%.">oTarget%</th>
<th title="When AR is ENABLED for the channel, keep pulling IN to the channel until its inbound liquidity falls below the iTarget%.">iTarget%</th>
<th title="When AR is ENABLED it will refill the channel with outbound liquidity.">AR</th>
<th onclick="sortTable(event.target, 18, 'String', 1)">Active</th>
<th onclick="sortTable(event.target, 22, 'String', 1)">Active</th>
</tr>
{% for channel in channels %}
<tr>
@ -136,6 +154,22 @@
<input type="hidden" name="update_target" value="0">
</form>
</td>
<td {% if channel.local_disabled == True %}style="background-color: rgba(248,81,73,0.15);"{% elif channel.private == True %}style="background-color: rgba(110,118,129,0.4)"{% endif %}>
<form action="/update_channel/" method="post">
{% csrf_token %}
<input data-tag="inbound" style="text-align:center" id="target" type="number" min="-100000" max="0" name="target" value="{{ channel.local_inbound_fee_rate }}">
<input type="hidden" name="chan_id" value="{{ channel.chan_id }}">
<input type="hidden" name="update_target" value="13">
</form>
</td>
<td {% if channel.local_disabled == True %}style="background-color: rgba(248,81,73,0.15);"{% elif channel.private == True %}style="background-color: rgba(110,118,129,0.4)"{% endif %}>
<form action="/update_channel/" method="post">
{% csrf_token %}
<input data-tag="inbound" style="text-align:center" id="target" type="number" min="-100000000" max="0" name="target" value="{{ channel.local_inbound_base_fee }}">
<input type="hidden" name="chan_id" value="{{ channel.chan_id }}">
<input type="hidden" name="update_target" value="12">
</form>
</td>
<td {% if channel.local_disabled == True %}style="background-color: rgba(248,81,73,0.15);"{% elif channel.private == True %}style="background-color: rgba(110,118,129,0.4)"{% endif %}>
<form action="/update_channel/" method="post">
{% csrf_token %}
@ -160,6 +194,8 @@
<input type="hidden" name="update_target" value="11">
</form>
</td>
<td {% if channel.remote_disabled == True %}style="background-color: rgba(248,81,73,0.15);"{% endif %}>{{ channel.remote_inbound_fee_rate|intcomma }}</td>
<td {% if channel.remote_disabled == True %}style="background-color: rgba(248,81,73,0.15);"{% endif %}>{{ channel.remote_inbound_base_fee|intcomma }}</td>
<td title="Fee Ratio: {{ channel.fee_ratio }}%" {% if channel.remote_disabled == True %}style="background-color: rgba(248,81,73,0.15);"{% endif %}>{{ channel.remote_fee_rate|intcomma }}</td>
<td {% if channel.remote_disabled == True %}style="background-color: rgba(248,81,73,0.15);"{% endif %}>{{ channel.remote_base_fee|intcomma }}</td>
<td>
@ -218,4 +254,15 @@
{% if local_settings %}
{% include 'local_settings.html' with settings=local_settings title='Update Local' %}
{% endif %}
{% endblock %}
<script>
document.addEventListener('DOMContentLoaded', async () => {
const node_info = await GET('node_info', {data: {limit:1}})
if (parseFloat(node_info.version.substring(0, 4)) <= 0.17){
let inputs = document.querySelectorAll('input[data-tag="inbound"]')
inputs.forEach(function(input) {
input.disabled = true
})
}
})
</script>
{% endblock %}

View file

@ -27,8 +27,8 @@
"amt_out": f => ({innerHTML: (f.amt_out > 1000 ? f.amt_out.intcomma() : f.amt_out) + ` <small class="w3-round w3-border-small w3-border-grey w3-tiny">+${f.fee.intcomma().toLocaleString()}</small>`, style: {paddingLeft: "0px", width: "160px"} }),
"chan_out_alias": f => ({innerHTML: f.chan_out_alias || f.chan_id_out}),
"chan_id_out": f => ({innerHTML: `<a href="/channel?=${f.chan_id_out}" target="_blank">${(BigInt(f.chan_id_out)>>40n)+'x'+(BigInt(f.chan_id_out)>>16n & BigInt('0xFFFFFF'))+'x'+(BigInt(f.chan_id_out) & BigInt('0xFFFF'))}</a>`}),
"fee": f => ({innerHTML: parseFloat(f.fee.toFixed(3)).toLocaleString()}),
"ppm": f => ({innerHTML: parseInt(f.ppm.toFixed(0)).toLocaleString()}),
"fee": f => ({innerHTML: parseFloat(f.fee.toFixed(3)).toLocaleString() + (f.inbound_fee > 0 ? ` <small class="w3-round w3-border-small w3-border-grey w3-tiny">-${f.inbound_fee.toLocaleString()}</small>` : ``)}),
"ppm": f => ({innerHTML: parseInt(f.ppm.toFixed(0)).toLocaleString() + (f.inbound_fee > 0 ? ` <small class="w3-round w3-border-small w3-border-grey w3-tiny">-${((f.inbound_fee/(f.inbound_fee+f.fee))*100).intcomma().toLocaleString()}%</small>` : ``)}),
}
let {fee, ppm} = routed_template
const payments_template = Object.assign({}, {

View file

@ -1770,6 +1770,30 @@ def update_channel(request):
db_channel.save()
Autofees(chan_id=db_channel.chan_id, peer_alias=db_channel.alias, setting=(f"Manual"), old_value=old_fee_rate, new_value=db_channel.local_fee_rate).save()
messages.success(request, 'Fee rate for channel ' + str(db_channel.alias) + ' (' + str(db_channel.chan_id) + ') updated to a value of: ' + str(target))
elif update_target == 12:
stub = lnrpc.LightningStub(lnd_connect())
version = stub.GetInfo(ln.GetInfoRequest()).version
if float(version[:4]) >= 0.18:
channel_point = point(db_channel)
inbound_fee_rate = db_channel.local_inbound_fee_rate if db_channel.local_inbound_fee_rate else 0
stub.UpdateChannelPolicy(ln.PolicyUpdateRequest(chan_point=channel_point, base_fee_msat=db_channel.local_base_fee, fee_rate=(db_channel.local_fee_rate/1000000), time_lock_delta=db_channel.local_cltv, inbound_fee=ln.InboundFee(base_fee_msat=target, fee_rate_ppm=inbound_fee_rate)))
db_channel.local_inbound_base_fee = target
db_channel.save()
messages.success(request, 'Inbound base fee for channel ' + str(db_channel.alias) + ' (' + str(db_channel.chan_id) + ') updated to a value of: ' + str(target))
else:
messages.error(request, f'LND version too low to set inbound fees, update to v0.18+')
elif update_target == 13:
stub = lnrpc.LightningStub(lnd_connect())
version = stub.GetInfo(ln.GetInfoRequest()).version
if float(version[:4]) >= 0.18:
channel_point = point(db_channel)
inbound_base_fee = db_channel.local_inbound_base_fee if db_channel.local_inbound_base_fee else 0
stub.UpdateChannelPolicy(ln.PolicyUpdateRequest(chan_point=channel_point, base_fee_msat=db_channel.local_base_fee, fee_rate=(db_channel.local_fee_rate/1000000), time_lock_delta=db_channel.local_cltv, inbound_fee=ln.InboundFee(base_fee_msat=inbound_base_fee, fee_rate_ppm=target)))
db_channel.local_inbound_fee_rate = target
db_channel.save()
messages.success(request, 'Inbound fee rate for channel ' + str(db_channel.alias) + ' (' + str(db_channel.chan_id) + ') updated to a value of: ' + str(target))
else:
messages.error(request, f'LND version too low to set inbound fees, update to v0.18+')
elif update_target == 2:
db_channel.ar_amt_target = target
db_channel.save()
@ -1924,6 +1948,36 @@ def update_setting(request):
db_channel.local_base_fee = target
db_channel.save()
messages.success(request, 'Base fee for all channels updated to a value of: ' + str(target))
elif key == 'ALL-iRate':
target = int(value)
stub = lnrpc.LightningStub(lnd_connect())
version = stub.GetInfo(ln.GetInfoRequest()).version
if float(version[:4]) >= 0.18:
channels = Channels.objects.filter(is_open=True)
for db_channel in channels:
channel_point = point(db_channel)
inbound_base_fee = db_channel.local_inbound_base_fee if db_channel.local_inbound_base_fee else 0
stub.UpdateChannelPolicy(ln.PolicyUpdateRequest(chan_point=channel_point, base_fee_msat=db_channel.local_base_fee, fee_rate=(db_channel.local_fee_rate/1000000), time_lock_delta=db_channel.local_cltv, inbound_fee=ln.InboundFee(base_fee_msat=inbound_base_fee, fee_rate_ppm=target)))
db_channel.local_inbound_fee_rate = target
db_channel.save()
messages.success(request, 'Inbound fee rate for all open channels updated to a value of: ' + str(target))
else:
messages.error(request, f'LND version too low to set inbound fees, update to v0.18+')
elif key == 'ALL-iBase':
target = int(value)
stub = lnrpc.LightningStub(lnd_connect())
version = stub.GetInfo(ln.GetInfoRequest()).version
if float(version[:4]) >= 0.18:
channels = Channels.objects.filter(is_open=True)
for db_channel in channels:
channel_point = point(db_channel)
inbound_fee_rate = db_channel.local_inbound_fee_rate if db_channel.local_inbound_fee_rate else 0
stub.UpdateChannelPolicy(ln.PolicyUpdateRequest(chan_point=channel_point, base_fee_msat=db_channel.local_base_fee, fee_rate=(db_channel.local_fee_rate/1000000), time_lock_delta=db_channel.local_cltv, inbound_fee=ln.InboundFee(base_fee_msat=target, fee_rate_ppm=inbound_fee_rate)))
db_channel.local_inbound_base_fee = target
db_channel.save()
messages.success(request, 'Inbound base fee for all channels updated to a value of: ' + str(target))
else:
messages.error(request, f'LND version too low to set inbound fees, update to v0.18+')
elif key == 'ALL-CLTV':
target = int(value)
stub = lnrpc.LightningStub(lnd_connect())
@ -2385,6 +2439,7 @@ def node_info(request):
except:
db_size = 0
return Response({
'version': node_info.version,
'num_peers': node_info.num_peers,
'synced_to_graph': node_info.synced_to_graph,
'synced_to_chain': node_info.synced_to_chain,
@ -2818,14 +2873,23 @@ def chan_policy(request):
channel_point = point(db_channel)
return_response = {}
try:
if serializer.validated_data['base_fee'] is not None or serializer.validated_data['fee_rate'] is not None or serializer.validated_data['cltv'] is not None or serializer.validated_data['min_htlc'] is not None or serializer.validated_data['max_htlc'] is not None:
if serializer.validated_data['base_fee'] is not None or serializer.validated_data['fee_rate'] is not None or serializer.validated_data['cltv'] is not None or serializer.validated_data['min_htlc'] is not None or serializer.validated_data['max_htlc'] is not None or serializer.validated_data['inbound_base_fee'] is not None or serializer.validated_data['inbound_fee_rate'] is not None:
base_fee_msat = serializer.validated_data['base_fee'] if serializer.validated_data['base_fee'] is not None else db_channel.local_base_fee
fee_rate = (serializer.validated_data['fee_rate']/1000000) if serializer.validated_data['fee_rate'] is not None else (db_channel.local_fee_rate/1000000)
inbound_base_fee_msat = serializer.validated_data['inbound_base_fee'] if serializer.validated_data['inbound_base_fee'] is not None else db_channel.local_inbound_base_fee
inbound_fee_rate = serializer.validated_data['inbound_fee_rate'] if serializer.validated_data['inbound_fee_rate'] is not None else db_channel.local_inbound_fee_rate
time_lock_delta = serializer.validated_data['cltv'] if serializer.validated_data['cltv'] is not None else db_channel.local_cltv
min_htlc_msat = int(serializer.validated_data['min_htlc']*1000) if serializer.validated_data['min_htlc'] is not None else db_channel.local_min_htlc_msat
max_htlc_msat = int(serializer.validated_data['max_htlc']*1000) if serializer.validated_data['max_htlc'] is not None else db_channel.local_max_htlc_msat
stub = lnrpc.LightningStub(lnd_connect())
stub.UpdateChannelPolicy(ln.PolicyUpdateRequest(chan_point=channel_point, base_fee_msat=base_fee_msat, fee_rate=fee_rate, time_lock_delta=time_lock_delta, min_htlc_msat_specified=True, min_htlc_msat=min_htlc_msat, max_htlc_msat=max_htlc_msat))
version = stub.GetInfo(ln.GetInfoRequest()).version
kwargs = {'chan_point':channel_point, 'base_fee_msat':base_fee_msat, 'fee_rate':fee_rate, 'time_lock_delta':time_lock_delta, 'min_htlc_msat_specified':True, 'min_htlc_msat':min_htlc_msat, 'max_htlc_msat':max_htlc_msat}
if serializer.validated_data['inbound_base_fee'] or serializer.validated_data['inbound_fee_rate']:
if float(version[:4]) >= 0.18:
kwargs['inbound_fee'] = ln.InboundFee(base_fee_msat = inbound_base_fee_msat if inbound_base_fee_msat else 0, fee_rate_ppm = inbound_fee_rate if inbound_fee_rate else 0)
else:
return Response({'error': f'LND version too low to set inbound fees, update to v0.18+'})
stub.UpdateChannelPolicy(ln.PolicyUpdateRequest(**kwargs))
if serializer.validated_data['base_fee'] is not None:
db_channel.local_base_fee = serializer.validated_data['base_fee']
db_channel.save()
@ -2837,6 +2901,14 @@ def chan_policy(request):
db_channel.save()
return_response['fee_rate'] = serializer.validated_data['fee_rate']
Autofees(chan_id=db_channel.chan_id, peer_alias=db_channel.alias, setting=(f"Manual"), old_value=old_fee_rate, new_value=db_channel.local_fee_rate).save()
if serializer.validated_data['inbound_base_fee'] is not None:
db_channel.local_inbound_base_fee = serializer.validated_data['inbound_base_fee']
db_channel.save()
return_response['inbound_base_fee'] = serializer.validated_data['inbound_base_fee']
if serializer.validated_data['inbound_fee_rate'] is not None:
db_channel.local_inbound_fee_rate = serializer.validated_data['inbound_fee_rate']
db_channel.save()
return_response['inbound_fee_rate'] = serializer.validated_data['inbound_fee_rate']
if serializer.validated_data['cltv'] is not None:
db_channel.local_cltv = serializer.validated_data['cltv']
db_channel.save()

60
jobs.py
View file

@ -149,11 +149,19 @@ def update_forwards(stub):
records = Forwards.objects.count()
forwards = stub.ForwardingHistory(ln.ForwardingHistoryRequest(start_time=1420070400, index_offset=records, num_max_events=100)).forwarding_events
for forward in forwards:
incoming_peer_alias = Channels.objects.filter(chan_id=forward.chan_id_in)[0].alias if Channels.objects.filter(chan_id=forward.chan_id_in).exists() else None
incoming_peer_alias = Channels.objects.filter(chan_id=forward.chan_id_in)[0].remote_pubkey[:12] if incoming_peer_alias == '' else incoming_peer_alias
outgoing_peer_alias = Channels.objects.filter(chan_id=forward.chan_id_out)[0].alias if Channels.objects.filter(chan_id=forward.chan_id_out).exists() else None
outgoing_peer_alias = Channels.objects.filter(chan_id=forward.chan_id_out)[0].remote_pubkey[:12] if outgoing_peer_alias == '' else outgoing_peer_alias
Forwards(forward_date=datetime.fromtimestamp(forward.timestamp), chan_id_in=forward.chan_id_in, chan_id_out=forward.chan_id_out, chan_in_alias=incoming_peer_alias, chan_out_alias=outgoing_peer_alias, amt_in_msat=forward.amt_in_msat, amt_out_msat=forward.amt_out_msat, fee=round(forward.fee_msat/1000, 3)).save()
inbound_channel = Channels.objects.get(chan_id=forward.chan_id_in) if Channels.objects.filter(chan_id=forward.chan_id_in).exists() else None
outbound_channel = Channels.objects.get(chan_id=forward.chan_id_out) if Channels.objects.filter(chan_id=forward.chan_id_out).exists() else None
forward_datetime = datetime.fromtimestamp(forward.timestamp)
amt_in_msat = forward.amt_in_msat
amt_out_msat = forward.amt_out_msat
in_fee_msat = 0
if outbound_channel and outbound_channel.fees_updated < forward_datetime:
out_fee_msat = int((amt_out_msat * (outbound_channel.local_fee_rate/1000000)) + outbound_channel.local_base_fee)
if forward.fee_msat < out_fee_msat:
in_fee_msat = out_fee_msat - forward.fee_msat
incoming_peer_alias = (inbound_channel.remote_pubkey[:12] if inbound_channel.alias == '' else inbound_channel.alias) if inbound_channel else forward.peer_alias_in
outgoing_peer_alias = (outbound_channel.remote_pubkey[:12] if outbound_channel.alias == '' else outbound_channel.alias) if outbound_channel else forward.peer_alias_out
Forwards(forward_date=forward_datetime, chan_id_in=forward.chan_id_in, chan_id_out=forward.chan_id_out, chan_in_alias=incoming_peer_alias, chan_out_alias=outgoing_peer_alias, amt_in_msat=amt_in_msat, amt_out_msat=amt_out_msat, fee=round(forward.fee_msat/1000, 3), inbound_fee=round(in_fee_msat/1000, 3)).save()
def disconnectpeer(stub, peer):
try:
@ -169,7 +177,9 @@ def update_channels(stub):
chan_list = []
channels = stub.ListChannels(ln.ListChannelsRequest()).channels
PendingHTLCs.objects.all().delete()
block_height = stub.GetInfo(ln.GetInfoRequest()).block_height
get_info = stub.GetInfo(ln.GetInfoRequest())
block_height = get_info.block_height
version = get_info.version
for channel in channels:
if Channels.objects.filter(chan_id=channel.chan_id).exists():
#Update the channel record with the most current data
@ -265,6 +275,16 @@ def update_channels(stub):
db_channel.local_disabled = local_policy.disabled
db_channel.local_min_htlc_msat = local_policy.min_htlc
db_channel.local_max_htlc_msat = local_policy.max_htlc_msat
if float(version[:4]) >= 0.18:
try:
db_channel.local_inbound_base_fee = local_policy.inbound_fee_base_msat
db_channel.local_inbound_fee_rate = local_policy.inbound_fee_rate_milli_msat
except:
db_channel.local_inbound_base_fee = 0
db_channel.local_inbound_fee_rate = 0
else:
db_channel.local_inbound_base_fee = 0
db_channel.local_inbound_fee_rate = 0
if db_channel.remote_cltv == -1:
PeerEvents(chan_id=db_channel.chan_id, peer_alias=db_channel.alias, event='BaseFee', old_value=None, new_value=remote_policy.fee_base_msat, out_liq=(db_channel.local_balance + db_channel.pending_outbound)).save()
db_channel.remote_base_fee = remote_policy.fee_base_msat
@ -281,6 +301,18 @@ def update_channels(stub):
db_channel.remote_min_htlc_msat = remote_policy.min_htlc
PeerEvents(chan_id=db_channel.chan_id, peer_alias=db_channel.alias, event='MaxHTLC', old_value=None, new_value=remote_policy.max_htlc_msat, out_liq=(db_channel.local_balance + db_channel.pending_outbound)).save()
db_channel.remote_max_htlc_msat = remote_policy.max_htlc_msat
if float(version[:4]) >= 0.18:
try:
PeerEvents(chan_id=db_channel.chan_id, peer_alias=db_channel.alias, event='IncomingBaseFee', old_value=None, new_value=remote_policy.inbound_fee_base_msat, out_liq=(db_channel.local_balance + db_channel.pending_outbound)).save()
db_channel.remote_inbound_base_fee = remote_policy.inbound_fee_base_msat
PeerEvents(chan_id=db_channel.chan_id, peer_alias=db_channel.alias, event='IncomingFeeRate', old_value=None, new_value=remote_policy.inbound_fee_rate_milli_msat, out_liq=(db_channel.local_balance + db_channel.pending_outbound)).save()
db_channel.remote_inbound_fee_rate = remote_policy.inbound_fee_rate_milli_msat
except:
db_channel.remote_inbound_base_fee = 0
db_channel.remote_inbound_fee_rate = 0
else:
db_channel.remote_inbound_base_fee = 0
db_channel.remote_inbound_fee_rate = 0
else:
if db_channel.remote_base_fee != remote_policy.fee_base_msat:
PeerEvents(chan_id=db_channel.chan_id, peer_alias=db_channel.alias, event='BaseFee', old_value=db_channel.remote_base_fee, new_value=remote_policy.fee_base_msat, out_liq=(db_channel.local_balance + db_channel.pending_outbound)).save()
@ -305,6 +337,22 @@ def update_channels(stub):
if db_channel.remote_max_htlc_msat != remote_policy.max_htlc_msat:
PeerEvents(chan_id=db_channel.chan_id, peer_alias=db_channel.alias, event='MaxHTLC', old_value=db_channel.remote_max_htlc_msat, new_value=remote_policy.max_htlc_msat, out_liq=(db_channel.local_balance + db_channel.pending_outbound)).save()
db_channel.remote_max_htlc_msat = remote_policy.max_htlc_msat
if float(version[:4]) >= 0.18:
if db_channel.remote_inbound_base_fee != remote_policy.inbound_fee_base_msat:
try:
PeerEvents(chan_id=db_channel.chan_id, peer_alias=db_channel.alias, event='IncomingBaseFee', old_value=db_channel.remote_inbound_base_fee, new_value=remote_policy.inbound_fee_base_msat, out_liq=(db_channel.local_balance + db_channel.pending_outbound)).save()
db_channel.remote_inbound_base_fee = remote_policy.inbound_fee_base_msat
except:
db_channel.remote_inbound_base_fee = 0
if db_channel.remote_inbound_fee_rate != remote_policy.inbound_fee_rate_milli_msat:
try:
PeerEvents(chan_id=db_channel.chan_id, peer_alias=db_channel.alias, event='IncomingFeeRate', old_value=db_channel.remote_inbound_fee_rate, new_value=remote_policy.inbound_fee_rate_milli_msat, out_liq=(db_channel.local_balance + db_channel.pending_outbound)).save()
db_channel.remote_inbound_fee_rate = remote_policy.inbound_fee_rate_milli_msat
except:
db_channel.remote_inbound_fee_rate = 0
else:
db_channel.remote_inbound_base_fee = 0
db_channel.remote_inbound_fee_rate = 0
except Exception as e: # LND has not found the channel on the graph
print(f"{datetime.now().strftime('%c')} : [Data] : Error getting graph data for channel {db_channel.chan_id}: {str(e)}")
if pending_channel: # skip adding new channel to the list, LND may not have added to the graph yet