-
Last 10 Payments Routed
+
Timestamp
@@ -337,7 +328,7 @@ Last 10 Rebalance Requests
{{ rebalance.value|intcomma }}
{{ rebalance.fee_limit|intcomma }}
{% if rebalance.target_alias == '' %}None Specified{% else %}{{ rebalance.target_alias }}{% endif %}
- {% if rebalance.status == 0 %}Pending{% elif rebalance.status == 1 %}In-Flight{% elif rebalance.status == 2 %}Successful{% elif rebalance.status == 3 %}Timeout{% elif rebalance.status == 4 %}No Route{% elif rebalance.status == 5 %}Error{% elif rebalance.status == 6 %}Incorrect Payment Details{% elif rebalance.status == 7 %}Insufficient Balance{% elif rebalance.status == 400 %}Rebalancer Request Failed{% elif rebalance.status == 408 %}Rebalancer Request Timeout{% else %}{{ rebalance.status }}{% endif %}
+ {% if rebalance.status == 0 %}Pending{% elif rebalance.status == 1 %}In-Flight{% elif rebalance.status == 2 %}Successful{% elif rebalance.status == 3 %}Timeout{% elif rebalance.status == 4 %}No Route{% elif rebalance.status == 5 %}Error{% elif rebalance.status == 6 %}Incorrect Payment Details{% elif rebalance.status == 7 %}Insufficient Balance{% elif rebalance.status == 400 %}Rebalancer Request Failed{% elif rebalance.status == 408 %}Rebalancer Request Timeout{% else %}{{ rebalance.status }}{% endif %}
{% endfor %}
@@ -345,7 +336,7 @@
Last 10 Rebalance Requests
{% endif %}
{% if payments %}
-
Last 5 Payments Sent
+
Timestamp
@@ -376,7 +367,7 @@ Last 5 Payments Sent
{% endif %}
{% if invoices %}
-
Last 5 Payments Received
+
Created
@@ -407,7 +398,7 @@ Last 5 Payments Received
{% endif %}
{% if failed_htlcs %}
-
Last 10 Failed HTLCs
+
Timestamp
@@ -467,9 +458,11 @@ Update Auto Rebalancer Settings
Target Outbound Above (%):
Global Max Fee Rate (ppm):
-
+
Max Cost (%):
+ Autopilot:
+
@@ -581,4 +574,4 @@ Update Peer Alias
-{% endblock %}
\ No newline at end of file
+{% endblock %}
diff --git a/gui/templates/invoices.html b/gui/templates/invoices.html
new file mode 100644
index 00000000..b46fa047
--- /dev/null
+++ b/gui/templates/invoices.html
@@ -0,0 +1,41 @@
+{% extends "base.html" %}
+{% block title %} {{ block.super }} - Invoices{% endblock %}
+{% block content %}
+{% load humanize %}
+{% if invoices %}
+
+
Last 150 Invoices
+
+
+ Created
+ Settled
+ Payment Hash
+ Value
+ Amount Paid
+ State
+ Channel In Alias
+ Channel In
+ Keysend
+
+ {% for invoice in invoices %}
+
+ {{ invoice.creation_date|naturaltime }}
+ {% if invoice.state == 1 %}{{ invoice.settle_date|naturaltime }}{% else %}N/A{% endif %}
+ {{ invoice.r_hash }}
+ {{ invoice.value|add:"0"|intcomma }}
+ {% if invoice.state == 1 %}{{ invoice.amt_paid|intcomma }}{% else %}N/A{% endif %}
+ {% if invoice.state == 0 %}Open{% elif invoice.state == 1 %}Settled{% elif invoice.state == 2 %}Canceled{% else %}{{ invoice.state }}{% endif %}
+ {{ invoice.chan_in_alias }}
+ {{ invoice.chan_in }}
+ {% if invoice.keysend_preimage != None %}Yes{% else %}No{% endif %}
+
+ {% endfor %}
+
+
+{% endif %}
+{% if not invoices %}
+
+
You dont have any invoices yet.
+
+{% endif %}
+{% endblock %}
\ No newline at end of file
diff --git a/gui/templates/keysends.html b/gui/templates/keysends.html
index 4f1c0881..3104ab99 100644
--- a/gui/templates/keysends.html
+++ b/gui/templates/keysends.html
@@ -1,4 +1,5 @@
{% extends "base.html" %}
+{% block title %} {{ block.super }} - Keysends{% endblock %}
{% block content %}
{% load humanize %}
{% if keysends %}
diff --git a/gui/templates/open_list.html b/gui/templates/open_list.html
index af80cb27..7825e23a 100644
--- a/gui/templates/open_list.html
+++ b/gui/templates/open_list.html
@@ -1,4 +1,5 @@
{% extends "base.html" %}
+{% block title %} {{ block.super }} - Opens{% endblock %}
{% block content %}
{% load humanize %}
{% if open_list %}
@@ -11,13 +12,13 @@ Suggested Open List
Successful Payments Routed
Amount Routed
Fees Paid
- Effective PPM
- Volume Score
- Savings By Volume
+ Effective PPM
+ Volume Score
+ Savings By Volume
{% for node in open_list %}
- {{ node.node_pubkey }}
+ {{ node.node_pubkey }}
{{ node.alias }}
{{ node.count }}
{{ node.amount|add:"0"|intcomma }}
@@ -30,4 +31,9 @@ Suggested Open List
{% endif %}
+{% if not open_list %}
+
+
No potential peers can be calculated yet, try waiting until you have some payment data.
+
+{% endif %}
{% endblock %}
\ No newline at end of file
diff --git a/gui/templates/payments.html b/gui/templates/payments.html
new file mode 100644
index 00000000..48dc8173
--- /dev/null
+++ b/gui/templates/payments.html
@@ -0,0 +1,41 @@
+{% extends "base.html" %}
+{% block title %} {{ block.super }} - Payments{% endblock %}
+{% block content %}
+{% load humanize %}
+{% if payments %}
+
+
Last 150 Payments
+
+
+ Timestamp
+ Hash
+ Value
+ Fee Paid
+ Status
+ Chan Out Alias
+ Chan Out ID
+ Route
+ Keysend
+
+ {% for payment in payments %}
+
+ {{ payment.creation_date|naturaltime }}
+ {{ payment.payment_hash }}
+ {{ payment.value|add:"0"|intcomma }}
+ {{ payment.fee|intcomma }}
+ {% if payment.status == 1 %}In-Flight{% elif payment.status == 2 %}Succeeded{% elif payment.status == 3 %}Failed{% else %}{{ payment.status }}{% endif %}
+ {% if payment.status == 2 %}{{ payment.chan_out_alias }}{% else %}N/A{% endif %}
+ {% if payment.status == 2 %}{{ payment.chan_out }}{% else %}N/A{% endif %}
+ {% if payment.status == 2 %}Open {% else %}N/A{% endif %}
+ {% if payment.keysend_preimage != None %}Yes{% else %}No{% endif %}
+
+ {% endfor %}
+
+
+{% endif %}
+{% if not payments %}
+
+
You dont have any payments yet.
+
+{% endif %}
+{% endblock %}
\ No newline at end of file
diff --git a/gui/templates/peers.html b/gui/templates/peers.html
index c600a142..b6e797fa 100644
--- a/gui/templates/peers.html
+++ b/gui/templates/peers.html
@@ -1,4 +1,5 @@
{% extends "base.html" %}
+{% block title %} {{ block.super }} - Peers{% endblock %}
{% block content %}
{% load humanize %}
{% if peers %}
@@ -14,7 +15,7 @@ Peers List
{% for peer in peers %}
- {{ peer.pubkey }}
+ {{ peer.pubkey }}
{{ peer.address }}
{{ peer.inbound }}
{{ peer.sat_sent|intcomma }}
@@ -24,4 +25,9 @@ Peers List
{% endif %}
+{% if not peers %}
+
+
No connected peers found!
+
+{% endif %}
{% endblock %}
\ No newline at end of file
diff --git a/gui/templates/pending_htlcs.html b/gui/templates/pending_htlcs.html
index 9061a20a..ab591981 100644
--- a/gui/templates/pending_htlcs.html
+++ b/gui/templates/pending_htlcs.html
@@ -1,4 +1,5 @@
{% extends "base.html" %}
+{% block title %} {{ block.super }} - HTLCs{% endblock %}
{% block content %}
{% load humanize %}
{% if outgoing_htlcs %}
@@ -55,4 +56,9 @@ Incoming HTLCs
{% endif %}
+{% if not outgoing_htlcs and not incoming_htlcs %}
+
+
No pending HTLCs were found!
+
+{% endif %}
{% endblock %}
\ No newline at end of file
diff --git a/gui/templates/route.html b/gui/templates/route.html
index 2affa7fb..dce393c5 100644
--- a/gui/templates/route.html
+++ b/gui/templates/route.html
@@ -1,6 +1,8 @@
{% extends "base.html" %}
+{% block title %} {{ block.super }} - Routes{% endblock %}
{% block content %}
{% load humanize %}
+{% if route %}
Route For Payment: {{ payment_hash }}
@@ -8,22 +10,30 @@ Route For Payment: {{ payment_hash }}
Step
Amount
Fee
+ PPM
+ Cost To
Alias
Channel ID
Channel Capacity
- Cost To
{% for hop in route %}
{{ hop.step }}
{{ hop.amt }}
{{ hop.fee }}
+ {{ hop.ppm }}
+ {{ hop.cost_to }}
{{ hop.alias }}
{{ hop.chan_id }}
{{ hop.chan_capacity|intcomma }}
- {{ hop.cost_to }}
{% endfor %}
+{% endif %}
+{% if not route %}
+
+
A route was not found for this payment hash!
+
+{% endif %}
{% endblock %}
\ No newline at end of file
diff --git a/gui/urls.py b/gui/urls.py
index 549726b8..a4f78d38 100644
--- a/gui/urls.py
+++ b/gui/urls.py
@@ -22,6 +22,10 @@
path('peers', views.peers, name='peers'),
path('balances', views.balances, name='balances'),
path('pending_htlcs', views.pending_htlcs, name='pending-htlcs'),
+ path('failed_htlcs', views.failed_htlcs, name='failed-htlcs'),
+ path('payments', views.payments, name='payments'),
+ path('invoices', views.invoices, name='invoices'),
+ path('forwards', views.forwards, name='forwards'),
path('openchannel/', views.open_channel_form, name='open-channel-form'),
path('closechannel/', views.close_channel_form, name='close-channel-form'),
path('connectpeer/', views.connect_peer_form, name='connect-peer-form'),
@@ -30,11 +34,14 @@
path('rebalancer/', views.rebalance, name='rebalancer'),
path('updatechanpolicy/', views.update_chan_policy, name='updatechanpolicy'),
path('autorebalance/', views.auto_rebalance, name='auto-rebalance'),
- path('ar_target/', views.ar_target, name='ar-target'),
+ path('update_channel/', views.update_channel, name='update-channel'),
+ path('update_setting/', views.update_setting, name='update-setting'),
path('suggested_opens/', views.suggested_opens, name='suggested-opens'),
path('suggested_actions/', views.suggested_actions, name='suggested-actions'),
path('keysends/', views.keysends, name='keysends'),
+ path('channels/', views.channels, name='channels'),
path('autopilot/', views.autopilot, name='autopilot'),
+ path('advanced/', views.advanced, name='advanced'),
path('api/', include(router.urls), name='api-root'),
path('api-auth/', include('rest_framework.urls'), name='api-auth'),
path('api/connectpeer/', views.connect_peer, name='connect-peer'),
diff --git a/gui/views.py b/gui/views.py
index 87079839..ce4c93ca 100644
--- a/gui/views.py
+++ b/gui/views.py
@@ -1,6 +1,6 @@
from django.contrib import messages
from django.shortcuts import get_object_or_404, render, redirect
-from django.db.models import Sum, IntegerField, Count
+from django.db.models import Sum, IntegerField, Count, F
from django.db.models.functions import Round
from django.contrib.auth.decorators import login_required
from django.conf import settings
@@ -8,7 +8,7 @@
from rest_framework import viewsets
from rest_framework.response import Response
from rest_framework.decorators import api_view
-from .forms import OpenChannelForm, CloseChannelForm, ConnectPeerForm, AddInvoiceForm, RebalancerForm, ChanPolicyForm, AutoRebalanceForm, ARTarget
+from .forms import OpenChannelForm, CloseChannelForm, ConnectPeerForm, AddInvoiceForm, RebalancerForm, ChanPolicyForm, UpdateChannel, UpdateSetting, AutoRebalanceForm
from .models import Payments, PaymentHops, Invoices, Forwards, Channels, Rebalancer, LocalSettings, Peers, Onchain, PendingHTLCs, FailedHTLCs, Autopilot
from .serializers import ConnectPeerSerializer, FailedHTLCSerializer, LocalSettingsSerializer, OpenChannelSerializer, CloseChannelSerializer, AddInvoiceSerializer, PaymentHopsSerializer, PaymentSerializer, InvoiceSerializer, ForwardSerializer, ChannelSerializer, PendingHTLCSerializer, RebalancerSerializer, UpdateAliasSerializer, PeerSerializer, OnchainSerializer, PendingHTLCs, FailedHTLCs
from .lnd_deps import lightning_pb2 as ln
@@ -16,6 +16,23 @@
from .lnd_deps.lnd_connect import lnd_connect
from lndg.settings import LND_NETWORK, LND_DIR_PATH
from os import path
+from pandas import DataFrame
+
+def graph_links():
+ if LocalSettings.objects.filter(key='GUI-GraphLinks').exists():
+ graph_links = str(LocalSettings.objects.filter(key='GUI-GraphLinks')[0].value)
+ else:
+ LocalSettings(key='GUI-GraphLinks', value='https://1ml.com').save()
+ graph_links = 'https://1ml.com'
+ return graph_links
+
+def network_links():
+ if LocalSettings.objects.filter(key='GUI-NetLinks').exists():
+ network_links = str(LocalSettings.objects.filter(key='GUI-NetLinks')[0].value)
+ else:
+ LocalSettings(key='GUI-NetLinks', value='https://mempool.space').save()
+ network_links = 'https://mempool.space'
+ return network_links
@login_required(login_url='/lndg-admin/login/?next=/')
def home(request):
@@ -41,26 +58,36 @@ def home(request):
total_received = 0 if total_invoices == 0 else invoices.aggregate(Sum('amt_paid'))['amt_paid__sum']
#Get recorded forwarding events
forwards = Forwards.objects.all().annotate(amt_in=Sum('amt_in_msat')/1000).annotate(amt_out=Sum('amt_out_msat')/1000).annotate(ppm=Round((Sum('fee')*1000000000)/Sum('amt_out_msat'), output_field=IntegerField())).order_by('-id')
- total_forwards = forwards.count()
- total_value_forwards = 0 if total_forwards == 0 else int(forwards.aggregate(Sum('amt_out_msat'))['amt_out_msat__sum']/1000)
- total_earned = 0 if total_forwards == 0 else forwards.aggregate(Sum('fee'))['fee__sum']
+ forwards_df = DataFrame.from_records(forwards.values())
+ total_forwards = forwards_df.shape[0]
+ total_value_forwards = 0 if total_forwards == 0 else int(forwards_df['amt_out_msat'].sum()/1000)
+ total_earned = 0 if total_forwards == 0 else forwards_df['fee'].sum()
+ forwards_df_in_sum = DataFrame() if forwards_df.empty else forwards_df.groupby('chan_id_in', as_index=True).sum()
+ forwards_df_out_sum = DataFrame() if forwards_df.empty else forwards_df.groupby('chan_id_out', as_index=True).sum()
+ forwards_df_in_count = DataFrame() if forwards_df.empty else forwards_df.groupby('chan_id_in', as_index=True).count()
+ forwards_df_out_count = DataFrame() if forwards_df.empty else forwards_df.groupby('chan_id_out', as_index=True).count()
#Get current active channels
active_channels = Channels.objects.filter(is_active=True, is_open=True).annotate(outbound_percent=(Sum('local_balance')*1000)/Sum('capacity')).annotate(inbound_percent=(Sum('remote_balance')*1000)/Sum('capacity')).order_by('outbound_percent')
total_capacity = 0 if active_channels.count() == 0 else active_channels.aggregate(Sum('capacity'))['capacity__sum']
total_inbound = 0 if total_capacity == 0 else active_channels.aggregate(Sum('remote_balance'))['remote_balance__sum']
total_outbound = 0 if total_capacity == 0 else active_channels.aggregate(Sum('local_balance'))['local_balance__sum']
total_unsettled = 0 if total_capacity == 0 else active_channels.aggregate(Sum('unsettled_balance'))['unsettled_balance__sum']
- detailed_active_channels = []
filter_7day = datetime.now() - timedelta(days=7)
- routed_7day = forwards.filter(forward_date__gte=filter_7day).count()
- routed_7day_amt = 0 if routed_7day == 0 else int(forwards.filter(forward_date__gte=filter_7day).aggregate(Sum('amt_out_msat'))['amt_out_msat__sum']/1000)
- total_earned_7day = 0 if routed_7day == 0 else forwards.filter(forward_date__gte=filter_7day).aggregate(Sum('fee'))['fee__sum']
+ forwards_df_7d = DataFrame.from_records(forwards.filter(forward_date__gte=filter_7day).values())
+ forwards_df_in_7d_sum = DataFrame() if forwards_df_7d.empty else forwards_df_7d.groupby('chan_id_in', as_index=True).sum()
+ forwards_df_out_7d_sum = DataFrame() if forwards_df_7d.empty else forwards_df_7d.groupby('chan_id_out', as_index=True).sum()
+ forwards_df_in_7d_count = DataFrame() if forwards_df_7d.empty else forwards_df_7d.groupby('chan_id_in', as_index=True).count()
+ forwards_df_out_7d_count = DataFrame() if forwards_df_7d.empty else forwards_df_7d.groupby('chan_id_out', as_index=True).count()
+ routed_7day = forwards_df_7d.shape[0]
+ routed_7day_amt = 0 if routed_7day == 0 else int(forwards_df_7d['amt_out_msat'].sum()/1000)
+ total_earned_7day = 0 if routed_7day == 0 else forwards_df_7d['fee'].sum()
payments_7day = payments.filter(status=2).filter(creation_date__gte=filter_7day)
payments_7day_amt = 0 if payments_7day.count() == 0 else payments_7day.aggregate(Sum('value'))['value__sum']
total_7day_fees = 0 if payments_7day.count() == 0 else payments_7day.aggregate(Sum('fee'))['fee__sum']
pending_htlcs = PendingHTLCs.objects.all()
pending_htlc_count = pending_htlcs.count()
pending_outbound = 0 if pending_htlcs.filter(incoming=False).count() == 0 else pending_htlcs.filter(incoming=False).aggregate(Sum('amount'))['amount__sum']
+ detailed_active_channels = []
for channel in active_channels:
detailed_channel = {}
detailed_channel['remote_pubkey'] = channel.remote_pubkey
@@ -79,17 +106,17 @@ def home(request):
detailed_channel['output_index'] = channel.output_index
detailed_channel['outbound_percent'] = int(round(channel.outbound_percent/10, 0))
detailed_channel['inbound_percent'] = int(round(channel.inbound_percent/10, 0))
- detailed_channel['routed_in'] = forwards.filter(chan_id_in=channel.chan_id).count()
- detailed_channel['routed_out'] = forwards.filter(chan_id_out=channel.chan_id).count()
- detailed_channel['amt_routed_in'] = 0 if detailed_channel['routed_in'] == 0 else int(forwards.filter(chan_id_in=channel.chan_id).aggregate(Sum('amt_in_msat'))['amt_in_msat__sum']/10000000)/100
- detailed_channel['amt_routed_out'] = 0 if detailed_channel['routed_out'] == 0 else int(forwards.filter(chan_id_out=channel.chan_id).aggregate(Sum('amt_out_msat'))['amt_out_msat__sum']/10000000)/100
- detailed_channel['routed_in_7day'] = forwards.filter(forward_date__gte=filter_7day).filter(chan_id_in=channel.chan_id).count()
- detailed_channel['routed_out_7day'] = forwards.filter(forward_date__gte=filter_7day).filter(chan_id_out=channel.chan_id).count()
- detailed_channel['amt_routed_in_7day'] = 0 if detailed_channel['routed_in_7day'] == 0 else int(forwards.filter(forward_date__gte=filter_7day).filter(chan_id_in=channel.chan_id).aggregate(Sum('amt_in_msat'))['amt_in_msat__sum']/10000000)/100
- detailed_channel['amt_routed_out_7day'] = 0 if detailed_channel['routed_out_7day'] == 0 else int(forwards.filter(forward_date__gte=filter_7day).filter(chan_id_out=channel.chan_id).aggregate(Sum('amt_out_msat'))['amt_out_msat__sum']/10000000)/100
+ detailed_channel['routed_in'] = forwards_df_in_count.loc[channel.chan_id].amt_out_msat if (forwards_df_in_count.index == channel.chan_id).any() else 0
+ detailed_channel['routed_out'] = forwards_df_out_count.loc[channel.chan_id].amt_out_msat if (forwards_df_out_count.index == channel.chan_id).any() else 0
+ detailed_channel['amt_routed_in'] = int(forwards_df_in_sum.loc[channel.chan_id].amt_out_msat//10000000)/100 if (forwards_df_in_sum.index == channel.chan_id).any() else 0
+ detailed_channel['amt_routed_out'] = int(forwards_df_out_sum.loc[channel.chan_id].amt_out_msat//10000000)/100 if (forwards_df_out_sum.index == channel.chan_id).any() else 0
+ detailed_channel['routed_in_7day'] = forwards_df_in_7d_count.loc[channel.chan_id].amt_out_msat if (forwards_df_in_7d_count.index == channel.chan_id).any() else 0
+ detailed_channel['routed_out_7day'] = forwards_df_out_7d_count.loc[channel.chan_id].amt_out_msat if (forwards_df_out_7d_count.index == channel.chan_id).any() else 0
+ detailed_channel['amt_routed_in_7day'] = int(forwards_df_in_7d_sum.loc[channel.chan_id].amt_out_msat//10000000)/100 if (forwards_df_in_7d_sum.index == channel.chan_id).any() else 0
+ detailed_channel['amt_routed_out_7day'] = int(forwards_df_out_7d_sum.loc[channel.chan_id].amt_out_msat//10000000)/100 if (forwards_df_out_7d_sum.index == channel.chan_id).any() else 0
detailed_channel['htlc_count'] = pending_htlcs.filter(chan_id=channel.chan_id).count()
detailed_channel['auto_rebalance'] = channel.auto_rebalance
- detailed_channel['ar_target'] = channel.ar_target
+ detailed_channel['ar_in_target'] = channel.ar_in_target
detailed_active_channels.append(detailed_channel)
#Get current inactive channels
inactive_channels = Channels.objects.filter(is_active=False, is_open=True).annotate(outbound_percent=(Sum('local_balance')*100)/Sum('capacity')).annotate(inbound_percent=(Sum('remote_balance')*100)/Sum('capacity')).order_by('-local_fee_rate').order_by('outbound_percent')
@@ -103,7 +130,7 @@ def home(request):
#Get list of recent rebalance requests
rebalances = Rebalancer.objects.all().order_by('-requested')
total_channels = node_info.num_active_channels + node_info.num_inactive_channels
- local_settings = LocalSettings.objects.all()
+ local_settings = LocalSettings.objects.filter(key__contains='AR-')
try:
db_size = round(path.getsize(path.expanduser(LND_DIR_PATH + '/data/graph/' + LND_NETWORK + '/channel.db'))*0.000000001, 3)
except:
@@ -120,7 +147,7 @@ def home(request):
'invoices': invoices[:6],
'total_received': total_received,
'total_invoices': total_invoices,
- 'forwards': forwards[:15],
+ 'forwards': forwards_df.head(15).to_dict(orient='records'),
'earned': int(total_earned),
'total_forwards': total_forwards,
'total_value_forwards': total_value_forwards,
@@ -158,19 +185,111 @@ def home(request):
'7day_payments_ppm': 0 if payments_7day_amt == 0 else int((total_7day_fees/payments_7day_amt)*1000000),
'liq_ratio': 0 if total_outbound == 0 else int((total_inbound/sum_outbound)*100),
'network': 'testnet/' if LND_NETWORK == 'testnet' else '',
+ 'graph_links': graph_links(),
+ 'network_links': network_links(),
'db_size': db_size
}
return render(request, 'home.html', context)
else:
return redirect('home')
+@login_required(login_url='/lndg-admin/login/?next=/')
+def channels(request):
+ if request.method == 'GET':
+ filter_7day = datetime.now() - timedelta(days=7)
+ filter_30day = datetime.now() - timedelta(days=30)
+ forwards = Forwards.objects.filter(forward_date__gte=filter_30day)
+ payments = Payments.objects.filter(status=2).filter(creation_date__gte=filter_30day).filter(payment_hash__in=Invoices.objects.filter(state=1).filter(settle_date__gte=filter_30day).values_list('r_hash'))
+ invoices = Invoices.objects.filter(state=1).filter(settle_date__gte=filter_30day).filter(r_hash__in=payments.values_list('payment_hash'))
+ channels = Channels.objects.filter(is_open=True)
+ channels_df = DataFrame.from_records(channels.values())
+ if channels_df.shape[0] > 0:
+ forwards_df_30d = DataFrame.from_records(forwards.values())
+ forwards_df_7d = DataFrame.from_records(forwards.filter(forward_date__gte=filter_7day).values())
+ forwards_df_in_30d_sum = DataFrame() if forwards_df_30d.empty else forwards_df_30d.groupby('chan_id_in', as_index=True).sum()
+ forwards_df_out_30d_sum = DataFrame() if forwards_df_30d.empty else forwards_df_30d.groupby('chan_id_out', as_index=True).sum()
+ forwards_df_in_7d_sum = DataFrame() if forwards_df_7d.empty else forwards_df_7d.groupby('chan_id_in', as_index=True).sum()
+ forwards_df_out_7d_sum = DataFrame() if forwards_df_7d.empty else forwards_df_7d.groupby('chan_id_out', as_index=True).sum()
+ forwards_df_in_30d_count = DataFrame() if forwards_df_30d.empty else forwards_df_30d.groupby('chan_id_in', as_index=True).count()
+ forwards_df_out_30d_count = DataFrame() if forwards_df_30d.empty else forwards_df_30d.groupby('chan_id_out', as_index=True).count()
+ forwards_df_in_7d_count = DataFrame() if forwards_df_7d.empty else forwards_df_7d.groupby('chan_id_in', as_index=True).count()
+ forwards_df_out_7d_count = DataFrame() if forwards_df_7d.empty else forwards_df_7d.groupby('chan_id_out', as_index=True).count()
+ payments_df_30d = DataFrame.from_records(payments.values())
+ payments_df_7d = DataFrame.from_records(payments.filter(creation_date__gte=filter_7day).values())
+ payments_df_30d_sum = DataFrame() if payments_df_30d.empty else payments_df_30d.groupby('chan_out', as_index=True).sum()
+ payments_df_7d_sum = DataFrame() if payments_df_7d.empty else payments_df_7d.groupby('chan_out', as_index=True).sum()
+ payments_df_30d_count = DataFrame() if payments_df_30d.empty else payments_df_30d.groupby('chan_out', as_index=True).count()
+ payments_df_7d_count = DataFrame() if payments_df_7d.empty else payments_df_7d.groupby('chan_out', as_index=True).count()
+ invoices_df_30d = DataFrame.from_records(invoices.values())
+ invoices_df_7d = DataFrame.from_records(invoices.filter(settle_date__gte=filter_7day).values())
+ invoices_df_30d_sum = DataFrame() if invoices_df_30d.empty else invoices_df_30d.groupby('chan_in', as_index=True).sum()
+ invoices_df_7d_sum = DataFrame() if invoices_df_7d.empty else invoices_df_7d.groupby('chan_in', as_index=True).sum()
+ invoices_df_30d_count = DataFrame() if invoices_df_30d.empty else invoices_df_30d.groupby('chan_in', as_index=True).count()
+ invoices_df_7d_count = DataFrame() if invoices_df_7d.empty else invoices_df_7d.groupby('chan_in', as_index=True).count()
+ invoice_hashes_7d = DataFrame() if invoices_df_7d.empty else invoices_df_7d.groupby('chan_in', as_index=True)['r_hash'].apply(list)
+ invoice_hashes_30d = DataFrame() if invoices_df_30d.empty else invoices_df_30d.groupby('chan_in', as_index=True)['r_hash'].apply(list)
+ channels_df['capacity'] = channels_df.apply(lambda row: round(row.capacity/1000000, 1), axis=1)
+ channels_df['routed_in_7day'] = channels_df.apply(lambda row: forwards_df_in_7d_count.loc[row.chan_id].amt_out_msat if (forwards_df_in_7d_count.index == row.chan_id).any() else 0, axis=1)
+ channels_df['routed_out_7day'] = channels_df.apply(lambda row: forwards_df_out_7d_count.loc[row.chan_id].amt_out_msat if (forwards_df_out_7d_count.index == row.chan_id).any() else 0, axis=1)
+ channels_df['routed_in_30day'] = channels_df.apply(lambda row: forwards_df_in_30d_count.loc[row.chan_id].amt_out_msat if (forwards_df_in_30d_count.index == row.chan_id).any() else 0, axis=1)
+ channels_df['routed_out_30day'] = channels_df.apply(lambda row: forwards_df_out_30d_count.loc[row.chan_id].amt_out_msat if (forwards_df_out_30d_count.index == row.chan_id).any() else 0, axis=1)
+ channels_df['amt_routed_in_7day'] = channels_df.apply(lambda row: int(forwards_df_in_7d_sum.loc[row.chan_id].amt_out_msat/100000000)/10 if (forwards_df_in_7d_sum.index == row.chan_id).any() else 0, axis=1)
+ channels_df['amt_routed_out_7day'] = channels_df.apply(lambda row: int(forwards_df_out_7d_sum.loc[row.chan_id].amt_out_msat/100000000)/10 if (forwards_df_out_7d_sum.index == row.chan_id).any() else 0, axis=1)
+ channels_df['amt_routed_in_30day'] = channels_df.apply(lambda row: int(forwards_df_in_30d_sum.loc[row.chan_id].amt_out_msat/100000000)/10 if (forwards_df_in_30d_sum.index == row.chan_id).any() else 0, axis=1)
+ channels_df['amt_routed_out_30day'] = channels_df.apply(lambda row: int(forwards_df_out_30d_sum.loc[row.chan_id].amt_out_msat/100000000)/10 if (forwards_df_out_30d_sum.index == row.chan_id).any() else 0, axis=1)
+ channels_df['rebal_in_30day'] = channels_df.apply(lambda row: invoices_df_30d_count.loc[row.chan_id].amt_paid if invoices_df_30d_count.empty == False and (invoices_df_30d_count.index == row.chan_id).any() else 0, axis=1)
+ channels_df['rebal_out_30day'] = channels_df.apply(lambda row: payments_df_30d_count.loc[row.chan_id].value if payments_df_30d_count.empty == False and (payments_df_30d_count.index == row.chan_id).any() else 0, axis=1)
+ channels_df['amt_rebal_in_30day'] = channels_df.apply(lambda row: int(invoices_df_30d_sum.loc[row.chan_id].amt_paid/100000)/10 if invoices_df_30d_count.empty == False and (invoices_df_30d_sum.index == row.chan_id).any() else 0, axis=1)
+ channels_df['amt_rebal_out_30day'] = channels_df.apply(lambda row: int(payments_df_30d_sum.loc[row.chan_id].value/100000)/10 if payments_df_30d_count.empty == False and (payments_df_30d_sum.index == row.chan_id).any() else 0, axis=1)
+ channels_df['rebal_in_7day'] = channels_df.apply(lambda row: invoices_df_7d_count.loc[row.chan_id].amt_paid if invoices_df_7d_count.empty == False and (invoices_df_7d_count.index == row.chan_id).any() else 0, axis=1)
+ channels_df['rebal_out_7day'] = channels_df.apply(lambda row: payments_df_7d_count.loc[row.chan_id].value if payments_df_7d_count.empty == False and (payments_df_7d_count.index == row.chan_id).any() else 0, axis=1)
+ channels_df['amt_rebal_in_7day'] = channels_df.apply(lambda row: int(invoices_df_7d_sum.loc[row.chan_id].amt_paid/100000)/10 if invoices_df_7d_count.empty == False and (invoices_df_7d_sum.index == row.chan_id).any() else 0, axis=1)
+ channels_df['amt_rebal_out_7day'] = channels_df.apply(lambda row: int(payments_df_7d_sum.loc[row.chan_id].value/100000)/10 if payments_df_7d_count.empty == False and (payments_df_7d_sum.index == row.chan_id).any() else 0, axis=1)
+ channels_df['revenue_7day'] = channels_df.apply(lambda row: int(forwards_df_out_7d_sum.loc[row.chan_id].fee) if forwards_df_out_7d_sum.empty == False and (forwards_df_out_7d_sum.index == row.chan_id).any() else 0, axis=1)
+ channels_df['revenue_30day'] = channels_df.apply(lambda row: int(forwards_df_out_30d_sum.loc[row.chan_id].fee) if forwards_df_out_30d_sum.empty == False and (forwards_df_out_30d_sum.index == row.chan_id).any() else 0, axis=1)
+ channels_df['revenue_assist_7day'] = channels_df.apply(lambda row: int(forwards_df_in_7d_sum.loc[row.chan_id].fee) if forwards_df_in_7d_sum.empty == False and (forwards_df_in_7d_sum.index == row.chan_id).any() else 0, axis=1)
+ channels_df['revenue_assist_30day'] = channels_df.apply(lambda row: int(forwards_df_in_30d_sum.loc[row.chan_id].fee) if forwards_df_in_30d_sum.empty == False and (forwards_df_in_30d_sum.index == row.chan_id).any() else 0, axis=1)
+ channels_df['costs_7day'] = channels_df.apply(lambda row: 0 if row['rebal_in_7day'] == 0 else int(payments_df_7d.set_index('payment_hash', inplace=False).loc[invoice_hashes_7d[row.chan_id] if invoice_hashes_7d.empty == False and (invoice_hashes_7d.index == row.chan_id).any() else []]['fee'].sum()), axis=1)
+ channels_df['costs_30day'] = channels_df.apply(lambda row: 0 if row['rebal_in_30day'] == 0 else int(payments_df_30d.set_index('payment_hash', inplace=False).loc[invoice_hashes_30d[row.chan_id] if invoice_hashes_30d.empty == False and (invoice_hashes_30d.index == row.chan_id).any() else []]['fee'].sum()), axis=1)
+ channels_df['profits_7day'] = channels_df.apply(lambda row: 0 if row['revenue_7day'] == 0 else row['revenue_7day'] - row['costs_7day'], axis=1)
+ channels_df['profits_30day'] = channels_df.apply(lambda row: 0 if row['revenue_30day'] == 0 else row['revenue_30day'] - row['costs_30day'], axis=1)
+ channels_df['open_block'] = channels_df.apply(lambda row: row.chan_id>>40, axis=1)
+ context = {
+ 'channels': channels_df.to_dict(orient='records'),
+ 'network': 'testnet/' if LND_NETWORK == 'testnet' else '',
+ 'graph_links': graph_links(),
+ 'network_links': network_links()
+ }
+ return render(request, 'channels.html', context)
+ else:
+ return redirect('home')
+
+@login_required(login_url='/lndg-admin/login/?next=/')
+def advanced(request):
+ if request.method == 'GET':
+ channels = Channels.objects.filter(is_open=True).annotate(outbound_percent=(Sum('local_balance')*1000)/Sum('capacity')).annotate(inbound_percent=(Sum('remote_balance')*1000)/Sum('capacity')).order_by('-is_active', 'outbound_percent')
+ channels_df = DataFrame.from_records(channels.values())
+ if channels_df.shape[0] > 0:
+ channels_df['out_percent'] = channels_df.apply(lambda row: int(round(row['outbound_percent']/10, 0)), axis=1)
+ channels_df['in_percent'] = channels_df.apply(lambda row: int(round(row['inbound_percent']/10, 0)), axis=1)
+ context = {
+ 'channels': channels_df.to_dict(orient='records'),
+ 'local_settings': LocalSettings.objects.all(),
+ 'network': 'testnet/' if LND_NETWORK == 'testnet' else '',
+ 'graph_links': graph_links(),
+ 'network_links': network_links()
+ }
+ return render(request, 'advanced.html', context)
+ else:
+ return redirect('home')
+
@login_required(login_url='/lndg-admin/login/?next=/')
def route(request):
if request.method == 'GET':
payment_hash = request.GET.urlencode()[1:]
context = {
'payment_hash': payment_hash,
- 'route': PaymentHops.objects.filter(payment_hash=payment_hash)
+ 'route': PaymentHops.objects.filter(payment_hash=payment_hash).annotate(ppm=Round((Sum('fee')/Sum('amt'))*1000000, output_field=IntegerField()))
}
return render(request, 'route.html', context)
else:
@@ -180,7 +299,9 @@ def route(request):
def peers(request):
if request.method == 'GET':
context = {
- 'peers': Peers.objects.filter(connected=True)
+ 'peers': Peers.objects.filter(connected=True),
+ 'network': 'testnet/' if LND_NETWORK == 'testnet' else '',
+ 'graph_links': graph_links()
}
return render(request, 'peers.html', context)
else:
@@ -192,7 +313,9 @@ def balances(request):
stub = lnrpc.LightningStub(lnd_connect(settings.LND_DIR_PATH, settings.LND_NETWORK, settings.LND_RPC_SERVER))
context = {
'utxos': stub.ListUnspent(ln.ListUnspentRequest(min_confs=0, max_confs=9999999)).utxos,
- 'transactions': list(Onchain.objects.filter(block_height=0)) + list(Onchain.objects.exclude(block_height=0).order_by('-block_height'))
+ 'transactions': list(Onchain.objects.filter(block_height=0)) + list(Onchain.objects.exclude(block_height=0).order_by('-block_height')),
+ 'network': 'testnet/' if LND_NETWORK == 'testnet' else '',
+ 'network_links': network_links()
}
return render(request, 'balances.html', context)
else:
@@ -208,7 +331,9 @@ def suggested_opens(request):
payments_60day = Payments.objects.filter(creation_date__gte=filter_60day).values_list('payment_hash')
open_list = PaymentHops.objects.filter(payment_hash__in=payments_60day).exclude(node_pubkey=self_pubkey).exclude(node_pubkey__in=current_peers).values('node_pubkey', 'alias').annotate(ppm=(Sum('fee')/Sum('amt'))*1000000).annotate(score=Round((Round(Count('id')/5, output_field=IntegerField())+Round(Sum('amt')/500000, output_field=IntegerField()))/10, output_field=IntegerField())).annotate(count=Count('id')).annotate(amount=Sum('amt')).annotate(fees=Sum('fee')).annotate(sum_cost_to=Sum('cost_to')/(Sum('amt')/1000000)).order_by('-score', 'ppm')[:21]
context = {
- 'open_list': open_list
+ 'open_list': open_list,
+ 'network': 'testnet/' if LND_NETWORK == 'testnet' else '',
+ 'graph_links': graph_links()
}
return render(request, 'open_list.html', context)
else:
@@ -240,33 +365,36 @@ def suggested_actions(request):
result['i7D'] = 0 if result['routed_in_7day'] == 0 else int(forwards.filter(chan_id_in=channel.chan_id).aggregate(Sum('amt_in_msat'))['amt_in_msat__sum']/10000000)/100
result['o7D'] = 0 if result['routed_out_7day'] == 0 else int(forwards.filter(chan_id_out=channel.chan_id).aggregate(Sum('amt_out_msat'))['amt_out_msat__sum']/10000000)/100
result['auto_rebalance'] = channel.auto_rebalance
- result['ar_target'] = channel.ar_target
+ result['ar_target'] = channel.ar_in_target
if result['o7D'] > (result['i7D']*1.10) and result['outbound_percent'] > 75:
- print('Case 1: Pass')
+ #print('Case 1: Pass')
continue
elif result['o7D'] > (result['i7D']*1.10) and result['inbound_percent'] > 75 and channel.auto_rebalance == False:
if channel.local_fee_rate <= channel.remote_fee_rate:
- print('Case 6: Peer Fee Too High')
+ #print('Case 6: Peer Fee Too High')
result['output'] = 'Peer Fee Too High'
result['reason'] = 'o7D > i7D AND Inbound Liq > 75% AND Local Fee < Remote Fee'
continue
- print('Case 2: Enable AR')
+ #print('Case 2: Enable AR')
result['output'] = 'Enable AR'
result['reason'] = 'o7D > i7D AND Inbound Liq > 75%'
elif result['o7D'] < (result['i7D']*1.10) and result['outbound_percent'] > 75 and channel.auto_rebalance == True:
- print('Case 3: Disable AR')
+ #print('Case 3: Disable AR')
result['output'] = 'Disable AR'
result['reason'] = 'o7D < i7D AND Outbound Liq > 75%'
elif result['o7D'] < (result['i7D']*1.10) and result['inbound_percent'] > 75:
- print('Case 4: Pass')
+ #print('Case 4: Pass')
continue
else:
- print('Case 5: Pass')
+ #print('Case 5: Pass')
continue
if len(result) > 0:
action_list.append(result)
context = {
- 'action_list': action_list
+ 'action_list': action_list,
+ 'network': 'testnet/' if LND_NETWORK == 'testnet' else '',
+ 'graph_links': graph_links(),
+ 'network_links': network_links()
}
return render(request, 'action_list.html', context)
else:
@@ -283,6 +411,47 @@ def pending_htlcs(request):
else:
return redirect('home')
+@login_required(login_url='/lndg-admin/login/?next=/')
+def failed_htlcs(request):
+ if request.method == 'GET':
+ context = {
+ 'failed_htlcs': FailedHTLCs.objects.all().order_by('-timestamp')[:150],
+ }
+ return render(request, 'failed_htlcs.html', context)
+ else:
+ return redirect('home')
+
+@login_required(login_url='/lndg-admin/login/?next=/')
+def payments(request):
+ if request.method == 'GET':
+ context = {
+ 'payments': Payments.objects.filter(status=2).order_by('-creation_date')[:150],
+ }
+ return render(request, 'payments.html', context)
+ else:
+ return redirect('home')
+
+@login_required(login_url='/lndg-admin/login/?next=/')
+def invoices(request):
+ if request.method == 'GET':
+ context = {
+ 'invoices': Invoices.objects.filter(state=1).order_by('-creation_date')[:150],
+ }
+ return render(request, 'invoices.html', context)
+ else:
+ return redirect('home')
+
+@login_required(login_url='/lndg-admin/login/?next=/')
+def forwards(request):
+ if request.method == 'GET':
+ context = {
+ 'forwards': Forwards.objects.all().annotate(amt_in=Sum('amt_in_msat')/1000).annotate(amt_out=Sum('amt_out_msat')/1000).annotate(ppm=Round((Sum('fee')*1000000000)/Sum('amt_out_msat'), output_field=IntegerField())).order_by('-id')[:150],
+ }
+ return render(request, 'forwards.html', context)
+ else:
+ return redirect('home')
+
+
@login_required(login_url='/lndg-admin/login/?next=/')
def keysends(request):
if request.method == 'GET':
@@ -447,11 +616,11 @@ def rebalance(request):
form = RebalancerForm(request.POST)
if form.is_valid():
try:
- if Channels.objects.filter(is_active=True, is_open=True, remote_pubkey=form.cleaned_data['last_hop_pubkey']).exists():
+ if Channels.objects.filter(is_active=True, is_open=True, remote_pubkey=form.cleaned_data['last_hop_pubkey']).exists() or form.cleaned_data['last_hop_pubkey'] == '':
chan_ids = []
for channel in form.cleaned_data['outgoing_chan_ids']:
chan_ids.append(channel.chan_id)
- target_alias = Channels.objects.filter(is_active=True, is_open=True, remote_pubkey=form.cleaned_data['last_hop_pubkey'])[0].alias if Channels.objects.filter(is_active=True, is_open=True, remote_pubkey=form.cleaned_data['last_hop_pubkey']).exists() else None
+ target_alias = Channels.objects.filter(is_active=True, is_open=True, remote_pubkey=form.cleaned_data['last_hop_pubkey'])[0].alias if Channels.objects.filter(is_active=True, is_open=True, remote_pubkey=form.cleaned_data['last_hop_pubkey']).exists() else ''
Rebalancer(value=form.cleaned_data['value'], fee_limit=form.cleaned_data['fee_limit'], outgoing_chan_ids=chan_ids, last_hop_pubkey=form.cleaned_data['last_hop_pubkey'], target_alias=target_alias, duration=form.cleaned_data['duration']).save()
messages.success(request, 'Rebalancer request created!')
else:
@@ -494,7 +663,6 @@ def update_chan_policy(request):
messages.error(request, 'Error updating channel policies! Error: ' + error_msg)
else:
messages.error(request, 'Invalid Request. Please try again.')
- print(form.errors)
return redirect('home')
@login_required(login_url='/lndg-admin/login/?next=/')
@@ -521,7 +689,8 @@ def auto_rebalance(request):
db_percent_target = LocalSettings.objects.get(key='AR-Target%')
db_percent_target.value = target_percent
db_percent_target.save()
- messages.success(request, 'Updated auto rebalancer rebalance target percent setting to: ' + str(target_percent))
+ Channels.objects.all().update(ar_amt_target=Round(F('capacity')*target_percent, output_field=IntegerField()))
+ messages.success(request, 'Updated auto rebalancer target amount for all channels to: ' + str(target_percent))
if form.cleaned_data['target_time'] is not None:
target_time = form.cleaned_data['target_time']
try:
@@ -551,7 +720,8 @@ def auto_rebalance(request):
db_outbound_target = LocalSettings.objects.get(key='AR-Outbound%')
db_outbound_target.value = outbound_percent
db_outbound_target.save()
- messages.success(request, 'Updated auto rebalancer target outbound percent setting to: ' + str(outbound_percent))
+ Channels.objects.all().update(ar_out_target=(round(outbound_percent*100, 0)))
+ messages.success(request, 'Updated auto rebalancer target outbound percent setting for all channels to: ' + str(outbound_percent))
if form.cleaned_data['fee_rate'] is not None:
fee_rate = form.cleaned_data['fee_rate']
try:
@@ -572,24 +742,173 @@ def auto_rebalance(request):
db_max_cost.value = max_cost
db_max_cost.save()
messages.success(request, 'Updated auto rebalancer max cost setting to: ' + str(max_cost))
+ if form.cleaned_data['autopilot'] is not None:
+ autopilot = form.cleaned_data['autopilot']
+ try:
+ db_autopilot = LocalSettings.objects.get(key='AR-Autopilot')
+ except:
+ LocalSettings(key='AR-Autopilot', value='0').save()
+ db_autopilot = LocalSettings.objects.get(key='AR-Autopilot')
+ db_autopilot.value = autopilot
+ db_autopilot.save()
+ messages.success(request, 'Updated autopilot setting to: ' + str(autopilot))
else:
messages.error(request, 'Invalid Request. Please try again.')
- return redirect('home')
+ return redirect(request.META.get('HTTP_REFERER'))
@login_required(login_url='/lndg-admin/login/?next=/')
-def ar_target(request):
+def update_channel(request):
if request.method == 'POST':
- form = ARTarget(request.POST)
+ form = UpdateChannel(request.POST)
if form.is_valid() and Channels.objects.filter(chan_id=form.cleaned_data['chan_id']).exists():
chan_id = form.cleaned_data['chan_id']
- target = form.cleaned_data['ar_target']
+ target = form.cleaned_data['target']
+ update_target = int(form.cleaned_data['update_target'])
db_channel = Channels.objects.filter(chan_id=chan_id)[0]
- db_channel.ar_target = target
- db_channel.save()
- messages.success(request, 'Auto rebalancer inbound target for channel ' + str(chan_id) + ' updated to a value of: ' + str(target) + '%')
+ if update_target == 0:
+ stub = lnrpc.LightningStub(lnd_connect(settings.LND_DIR_PATH, settings.LND_NETWORK, settings.LND_RPC_SERVER))
+ channel_point = ln.ChannelPoint()
+ channel_point.funding_txid_bytes = bytes.fromhex(db_channel.funding_txid)
+ channel_point.funding_txid_str = db_channel.funding_txid
+ channel_point.output_index = db_channel.output_index
+ stub.UpdateChannelPolicy(ln.PolicyUpdateRequest(chan_point=channel_point, base_fee_msat=target, fee_rate=(db_channel.local_fee_rate/1000000), time_lock_delta=40))
+ db_channel.local_base_fee = target
+ db_channel.save()
+ messages.success(request, 'Base fee for channel ' + str(db_channel.alias) + ' (' + str(db_channel.chan_id) + ') updated to a value of: ' + str(target))
+ elif update_target == 1:
+ stub = lnrpc.LightningStub(lnd_connect(settings.LND_DIR_PATH, settings.LND_NETWORK, settings.LND_RPC_SERVER))
+ channel_point = ln.ChannelPoint()
+ channel_point.funding_txid_bytes = bytes.fromhex(db_channel.funding_txid)
+ channel_point.funding_txid_str = db_channel.funding_txid
+ channel_point.output_index = db_channel.output_index
+ stub.UpdateChannelPolicy(ln.PolicyUpdateRequest(chan_point=channel_point, base_fee_msat=db_channel.local_base_fee, fee_rate=(target/1000000), time_lock_delta=40))
+ db_channel.local_fee_rate = target
+ db_channel.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 == 2:
+ db_channel.ar_amt_target = target
+ db_channel.save()
+ messages.success(request, 'Auto rebalancer target amount for channel ' + str(db_channel.alias) + ' (' + str(db_channel.chan_id) + ') updated to a value of: ' + str(target))
+ elif update_target == 3:
+ db_channel.ar_in_target = target
+ db_channel.save()
+ messages.success(request, 'Auto rebalancer inbound target for channel ' + str(db_channel.alias) + ' (' + str(db_channel.chan_id) + ') updated to a value of: ' + str(target) + '%')
+ elif update_target == 4:
+ db_channel.ar_out_target = target
+ db_channel.save()
+ messages.success(request, 'Auto rebalancer outbound target for channel ' + str(db_channel.alias) + ' (' + str(db_channel.chan_id) + ') updated to a value of: ' + str(target) + '%')
+ elif update_target == 5:
+ db_channel.auto_rebalance = True if db_channel.auto_rebalance == False else False
+ db_channel.save()
+ messages.success(request, 'Auto rebalancer status for chanel ' + str(db_channel.alias) + ' (' + str(db_channel.chan_id) + ') updated to a value of: ' + str(db_channel.auto_rebalance))
else:
messages.error(request, 'Invalid Request. Please try again.')
- return redirect('home')
+ return redirect(request.META.get('HTTP_REFERER'))
+
+@login_required(login_url='/lndg-admin/login/?next=/')
+def update_setting(request):
+ if request.method == 'POST':
+ form = UpdateSetting(request.POST)
+ if form.is_valid():
+ key = form.cleaned_data['key']
+ value = form.cleaned_data['value']
+ if key == 'AR-Target%':
+ target_percent = value
+ try:
+ db_percent_target = LocalSettings.objects.get(key='AR-Target%')
+ except:
+ LocalSettings(key='AR-Target%', value='0.05').save()
+ db_percent_target = LocalSettings.objects.get(key='AR-Target%')
+ db_percent_target.value = target_percent
+ db_percent_target.save()
+ Channels.objects.all().update(ar_amt_target=Round(F('capacity')*target_percent, output_field=IntegerField()))
+ messages.success(request, 'Updated auto rebalancer target amount for all channels to: ' + str(target_percent))
+ elif key == 'AR-Time':
+ target_time = int(value)
+ try:
+ db_time_target = LocalSettings.objects.get(key='AR-Time')
+ except:
+ LocalSettings(key='AR-Time', value='5').save()
+ db_time_target = LocalSettings.objects.get(key='AR-Time')
+ db_time_target.value = target_time
+ db_time_target.save()
+ messages.success(request, 'Updated auto rebalancer target time setting to: ' + str(target_time))
+ elif key == 'AR-Enabled':
+ enabled = int(value)
+ try:
+ db_enabled = LocalSettings.objects.get(key='AR-Enabled')
+ except:
+ LocalSettings(key='AR-Enabled', value='0').save()
+ db_enabled = LocalSettings.objects.get(key='AR-Enabled')
+ db_enabled.value = enabled
+ db_enabled.save()
+ messages.success(request, 'Updated auto rebalancer enabled setting to: ' + str(enabled))
+ elif key == 'AR-Outbound%':
+ outbound_percent = float(value)
+ try:
+ db_outbound_target = LocalSettings.objects.get(key='AR-Outbound%')
+ except:
+ LocalSettings(key='AR-Outbound%', value='0.75').save()
+ db_outbound_target = LocalSettings.objects.get(key='AR-Outbound%')
+ db_outbound_target.value = outbound_percent
+ db_outbound_target.save()
+ Channels.objects.all().update(ar_out_target=(round(outbound_percent*100, 0)))
+ messages.success(request, 'Updated auto rebalancer target outbound percent setting for all channels to: ' + str(outbound_percent))
+ elif key == 'AR-MaxFeeRate':
+ fee_rate = int(value)
+ try:
+ db_fee_rate = LocalSettings.objects.get(key='AR-MaxFeeRate')
+ except:
+ LocalSettings(key='AR-MaxFeeRate', value='100').save()
+ db_fee_rate = LocalSettings.objects.get(key='AR-MaxFeeRate')
+ db_fee_rate.value = fee_rate
+ db_fee_rate.save()
+ messages.success(request, 'Updated auto rebalancer max fee rate setting to: ' + str(fee_rate))
+ elif key == 'AR-MaxCost%':
+ max_cost = float(value)
+ try:
+ db_max_cost = LocalSettings.objects.get(key='AR-MaxCost%')
+ except:
+ LocalSettings(key='AR-MaxCost%', value='0.50').save()
+ db_max_cost = LocalSettings.objects.get(key='AR-MaxCost%')
+ db_max_cost.value = max_cost
+ db_max_cost.save()
+ messages.success(request, 'Updated auto rebalancer max cost setting to: ' + str(max_cost))
+ elif key == 'AR-Autopilot':
+ autopilot = int(value)
+ try:
+ db_autopilot = LocalSettings.objects.get(key='AR-Autopilot')
+ except:
+ LocalSettings(key='AR-Autopilot', value='0').save()
+ db_autopilot = LocalSettings.objects.get(key='AR-Autopilot')
+ db_autopilot.value = autopilot
+ db_autopilot.save()
+ messages.success(request, 'Updated autopilot setting to: ' + str(autopilot))
+ elif key == 'GUI-GraphLinks':
+ links = str(value)
+ try:
+ db_links = LocalSettings.objects.get(key='GUI-GraphLinks')
+ except:
+ LocalSettings(key='GUI-GraphLinks', value='0').save()
+ db_links = LocalSettings.objects.get(key='GUI-GraphLinks')
+ db_links.value = links
+ db_links.save()
+ messages.success(request, 'Updated graph links to use: ' + str(links))
+ elif key == 'GUI-NetLinks':
+ links = str(value)
+ try:
+ db_links = LocalSettings.objects.get(key='GUI-NetLinks')
+ except:
+ LocalSettings(key='GUI-NetLinks', value='0').save()
+ db_links = LocalSettings.objects.get(key='GUI-NetLinks')
+ db_links.value = links
+ db_links.save()
+ messages.success(request, 'Updated network links to use: ' + str(links))
+ else:
+ messages.error(request, 'Invalid Request. Please try again.')
+ else:
+ messages.error(request, 'Invalid Request. Please try again.')
+ return redirect(request.META.get('HTTP_REFERER'))
class PaymentsViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Payments.objects.all()
@@ -659,7 +978,6 @@ def create(self, request):
serializer.save()
return Response(serializer.data)
else:
- print(serializer.errors)
return Response(serializer.errors)
@api_view(['POST'])
diff --git a/rebalancer.py b/rebalancer.py
index 26515dc3..99ec7082 100644
--- a/rebalancer.py
+++ b/rebalancer.py
@@ -1,5 +1,5 @@
import django, json, datetime
-from django.db.models import Sum
+from django.db.models import Sum, F
from datetime import datetime, timedelta
from gui.lnd_deps import lightning_pb2 as ln
from gui.lnd_deps import lightning_pb2_grpc as lnrpc
@@ -74,21 +74,15 @@ def auto_schedule():
LocalSettings(key='AR-Enabled', value='0').save()
enabled = 0
if enabled == 1:
- auto_rebalance_channels = Channels.objects.filter(is_active=True, is_open=True).annotate(percent_outbound=(Sum('local_balance')*100)/Sum('capacity')).annotate(inbound_can=((Sum('remote_balance')*100)/Sum('capacity'))/Sum('ar_target'))
+ auto_rebalance_channels = Channels.objects.filter(is_active=True, is_open=True).annotate(percent_outbound=(Sum('local_balance')*100)/Sum('capacity')).annotate(inbound_can=((Sum('remote_balance')*100)/Sum('capacity'))/Sum('ar_in_target'))
if len(auto_rebalance_channels) > 0:
- if LocalSettings.objects.filter(key='AR-Outbound%').exists():
- outbound_percent = int(float(LocalSettings.objects.filter(key='AR-Outbound%')[0].value) * 100)
- else:
+ if not LocalSettings.objects.filter(key='AR-Outbound%').exists():
LocalSettings(key='AR-Outbound%', value='0.75').save()
- outbound_percent = 0.75 * 100
- outbound_cans = list(auto_rebalance_channels.filter(auto_rebalance=False, percent_outbound__gte=outbound_percent).values_list('chan_id', flat=True))
+ outbound_cans = list(auto_rebalance_channels.filter(auto_rebalance=False, percent_outbound__gte=F('ar_out_target')).values_list('chan_id', flat=True))
inbound_cans = auto_rebalance_channels.filter(auto_rebalance=True, inbound_can__gte=1)
if len(inbound_cans) > 0 and len(outbound_cans) > 0:
- if LocalSettings.objects.filter(key='AR-Target%').exists():
- target_percent = float(LocalSettings.objects.filter(key='AR-Target%')[0].value)
- else:
+ if not LocalSettings.objects.filter(key='AR-Target%').exists():
LocalSettings(key='AR-Target%', value='0.05').save()
- target_percent = 0.05
if LocalSettings.objects.filter(key='AR-MaxFeeRate').exists():
max_fee_rate = int(LocalSettings.objects.filter(key='AR-MaxFeeRate')[0].value)
else:
@@ -104,7 +98,7 @@ def auto_schedule():
target_fee_rate = int(target.local_fee_rate * max_cost)
if target_fee_rate > 0 and target_fee_rate > target.remote_fee_rate:
value_per_fee = int(1 / (target_fee_rate / 1000000)) if target_fee_rate <= max_fee_rate else int(1 / (max_fee_rate / 1000000))
- target_value = int((target.capacity * target_percent) / value_per_fee) * value_per_fee
+ target_value = int(target.ar_amt_target / value_per_fee) * value_per_fee
if target_value >= value_per_fee:
if LocalSettings.objects.filter(key='AR-Time').exists():
target_time = int(LocalSettings.objects.filter(key='AR-Time')[0].value)
@@ -121,8 +115,7 @@ def auto_schedule():
print('Creating Auto Rebalance Request')
print('Request for:', target.chan_id)
print('Request routing through:', outbound_cans)
- print('Target % Of Value:', target_percent)
- print('Target Value:', target_value)
+ print('Target Value:', target.ar_amt_target)
print('Target Fee:', target_fee)
print('Target Time:', target_time)
Rebalancer(value=target_value, fee_limit=target_fee, outgoing_chan_ids=outbound_cans, last_hop_pubkey=inbound_pubkey.remote_pubkey, target_alias=inbound_pubkey.alias, duration=target_time).save()
@@ -145,21 +138,24 @@ def auto_enable():
i7D = 0 if routed_in_7day == 0 else int(forwards.filter(chan_id_in=channel.chan_id).aggregate(Sum('amt_in_msat'))['amt_in_msat__sum']/10000000)/100
o7D = 0 if routed_out_7day == 0 else int(forwards.filter(chan_id_out=channel.chan_id).aggregate(Sum('amt_out_msat'))['amt_out_msat__sum']/10000000)/100
if o7D > (i7D*1.10) and outbound_percent > 75:
- print('Case 1: Pass')
+ #print('Case 1: Pass')
+ pass
elif o7D > (i7D*1.10) and inbound_percent > 75 and channel.auto_rebalance == False:
- print('Case 2: Enable AR - o7D > i7D AND Inbound Liq > 75%')
+ #print('Case 2: Enable AR - o7D > i7D AND Inbound Liq > 75%')
channel.auto_rebalance = True
channel.save()
Autopilot(chan_id=channel.chan_id, peer_alias=channel.alias, setting='Enabled', old_value=0, new_value=1).save()
elif o7D < (i7D*1.10) and outbound_percent > 75 and channel.auto_rebalance == True:
- print('Case 3: Disable AR - o7D < i7D AND Outbound Liq > 75%')
+ #print('Case 3: Disable AR - o7D < i7D AND Outbound Liq > 75%')
channel.auto_rebalance = False
channel.save()
Autopilot(chan_id=channel.chan_id, peer_alias=channel.alias, setting='Enabled', old_value=1, new_value=0).save()
elif o7D < (i7D*1.10) and inbound_percent > 75:
- print('Case 4: Pass')
+ #print('Case 4: Pass')
+ pass
else:
- print('Case 5: Pass')
+ #print('Case 5: Pass')
+ pass
def main():
rebalances = Rebalancer.objects.filter(status=0).order_by('id')
diff --git a/requirements.txt b/requirements.txt
index f22fc5ee..d8d7fb48 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -3,4 +3,5 @@ djangorestframework
django-qr-code
grpcio
protobuf
-pytz
\ No newline at end of file
+pytz
+pandas
\ No newline at end of file
diff --git a/systemd.md b/systemd.md
index 733e1470..e367b2b7 100644
--- a/systemd.md
+++ b/systemd.md
@@ -93,6 +93,8 @@ User=
Group=
ExecStart=/usr/bin/bash /home//lndg/htlc_stream.sh
StandardError=append:/var/log/lnd_htlc_stream_error.log
+Restart=on-failure
+RestartSec=60s
```
Enable and start the service to run the htlc failure stream service file.
`sudo systemctl enable htlc-stream-lndg.service`