mirror of
https://github.com/cryptosharks131/lndg.git
synced 2026-08-13 12:33:04 +02:00
v1.0.2 (#27)
This commit is contained in:
parent
b3c28c9c02
commit
ea83dd324a
27 changed files with 981 additions and 166 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -3,4 +3,5 @@ db.sqlite3
|
|||
lndg/settings.py
|
||||
frontend
|
||||
node_modules
|
||||
lndg-admin.txt
|
||||
lndg-admin.txt
|
||||
.vscode
|
||||
47
README.md
47
README.md
|
|
@ -127,6 +127,31 @@ Alternatively, you may also make your own task for these files with your preferr
|
|||
You can serve the dashboard at all times using a webserver instead of the development server. Using a webserver will serve your static files and installing whitenoise is not required when running in this manner. Any webserver can be used to host the site if configured properly. A bash script has been included to help aide in the setup of a nginx webserver. `sudo bash nginx.sh`
|
||||
|
||||
## Key Features
|
||||
### Suggests New Peers
|
||||
LNDg will make suggestions for new peers to open channels to based on your node's successful routing history.
|
||||
#### There are two unique values in LNDg:
|
||||
1. Volume Score - A score based upon both the count of transactions and the volume of transactions routed through the peer
|
||||
2. Savings By Volume (ppm) - The amount of sats you could have saved during rebalances if you were peered directly with this node over the total amount routed through the peer
|
||||
|
||||
### Channel Performance Metrics
|
||||
#### LNDg will aggregate your payment and forwarding data to provide the following metrics:
|
||||
1. Outbound Flow Details - This shows the amount routed outbound next to the amount rebalanced in
|
||||
2. Revenue Details - This shows the revenue earned on the left, the profit (revenue - cost) in the middle and the assisted revenue (amount earned due to this channel's inbound flow) on the right
|
||||
3. Inbound Flow Details - This shows the amount routed inbound next to the amount rebalanced out
|
||||
4. Updates - This is the number of updates the channel has had and is directly correlated to the space it takes up in channel.db
|
||||
|
||||
### Password Protected Login
|
||||
The initial login username is `lndg-admin` but can be easily modified by going to the page found here: `/lndg-admin`
|
||||
|
||||
### Suggests AR Actions
|
||||
LNDg will make suggestions for actions to take around Auto-Rebalancing.
|
||||
|
||||
### AR-Autopilot Setting
|
||||
LNDg will automatically act upon the suggestions it makes on the Suggests AR Actions page.
|
||||
|
||||
### HTLC Failure Stream
|
||||
LNDg will listen for failure events in your htlc stream and record them to the dashboard when they occur.
|
||||
|
||||
### API Backend
|
||||
The following data can be accessed at the /api endpoint:
|
||||
`payments` `paymenthops` `invoices` `forwards` `onchain` `peers` `channels` `rebalancer` `settings` `pendinghtlcs` `failedhtlcs`
|
||||
|
|
@ -134,17 +159,8 @@ The following data can be accessed at the /api endpoint:
|
|||
### Peer Reconnection
|
||||
LNDg will automatically try to resolve any channels that are seen as inactive, no more than every 3 minutes per peer.
|
||||
|
||||
### Suggests New Peers
|
||||
LNDg will make suggestions for new peers to open channels to based on your node's successful routing history.
|
||||
|
||||
### Suggests AR Actions
|
||||
LNDg will make suggestions for actions to take around Auto-Rebalancing.
|
||||
|
||||
### HTLC Failure Stream
|
||||
LNDg will listen for failure events in your htlc stream and record them to the dashboard when they occur.
|
||||
|
||||
### Auto-Rebalancer
|
||||
Here are some notes to help you get started using the Auto-Rebalancer (AR).
|
||||
## Auto-Rebalancer
|
||||
### Here are some notes to help you get started using the Auto-Rebalancer (AR).
|
||||
|
||||
The objective of the Auto-Rebalancer is to "refill" the liquidity on the local side (i.e. OUTBOUND) of profitable and lucarative channels. So that, when a forward comes in from another node there is always enough liquidity to route the payment and in return collect the desired routing fees.
|
||||
|
||||
|
|
@ -179,10 +195,10 @@ The objective of the Auto-Rebalancer is to "refill" the liquidity on the local s
|
|||
```
|
||||
Enabled: 1
|
||||
Target Amount (%): 0.03
|
||||
Target Time (min): 5
|
||||
Target Time (min): 3
|
||||
Target Outbound Above (%): 0.4
|
||||
Global Max Fee Rate (ppm): 200
|
||||
Max Cost (%): 0.5
|
||||
Global Max Fee Rate (ppm): 500
|
||||
Max Cost (%): 0.6
|
||||
```
|
||||
3. Go to section Last 10 Rebalance Requests - that will show the list of the rebalancing queue and status.
|
||||
|
||||
|
|
@ -197,7 +213,8 @@ If you want a channel not to be picked for rebalancing (i.e. it is already full
|
|||

|
||||

|
||||
|
||||
### Peers, Balances, Routes, Keysends and Pending HTLCs All Open In Separate Screens
|
||||
### Channel Performance, Peers, Balances, Routes, Keysends and Pending HTLCs All Open In Separate Screens
|
||||

|
||||

|
||||

|
||||

|
||||
|
|
|
|||
19
gui/forms.py
19
gui/forms.py
|
|
@ -70,7 +70,22 @@ class AutoRebalanceForm(forms.Form):
|
|||
fee_rate = forms.IntegerField(label='fee_rate', required=False)
|
||||
outbound_percent = forms.FloatField(label='outbound_percent', required=False)
|
||||
max_cost = forms.FloatField(label='max_cost', required=False)
|
||||
autopilot = forms.IntegerField(label='autopilot', required=False)
|
||||
|
||||
class ARTarget(forms.Form):
|
||||
updates_channel_codes = [
|
||||
(0, 'base_fee'),
|
||||
(1, 'fee_rate'),
|
||||
(2, 'ar_amt_target'),
|
||||
(3, 'ar_in_target'),
|
||||
(4, 'ar_out_target'),
|
||||
(5, 'ar_enabled'),
|
||||
]
|
||||
|
||||
class UpdateChannel(forms.Form):
|
||||
chan_id = forms.IntegerField(label='chan_id')
|
||||
ar_target = forms.IntegerField(label='ar_target')
|
||||
target = forms.IntegerField(label='target')
|
||||
update_target = forms.ChoiceField(label='update_target', choices=updates_channel_codes)
|
||||
|
||||
class UpdateSetting(forms.Form):
|
||||
key = forms.CharField(label='setting', max_length=20)
|
||||
value = forms.CharField(label='value', max_length=50)
|
||||
53
gui/migrations/0018_auto_20220114_2218.py
Normal file
53
gui/migrations/0018_auto_20220114_2218.py
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
# Generated by Django 3.2.7 on 2022-01-14 22:18
|
||||
|
||||
from django.db import migrations, models
|
||||
from django.db.models import F, IntegerField
|
||||
from django.db.models.functions import Round
|
||||
|
||||
def update_defaults(apps, schedma_editor):
|
||||
channels = apps.get_model('gui', 'channels')
|
||||
settings = apps.get_model('gui', 'localsettings')
|
||||
try:
|
||||
if settings.objects.filter(key='AR-Target%').exists():
|
||||
ar_amt_target = float(settings.objects.filter(key='AR-Target%')[0].value)
|
||||
else:
|
||||
ar_amt_target = 0.05
|
||||
channels.objects.all().update(ar_amt_target=Round(F('capacity')*ar_amt_target, output_field=IntegerField()))
|
||||
except Exception as e:
|
||||
print('Migration step failed:', str(e))
|
||||
try:
|
||||
if settings.objects.filter(key='AR-Outbound%').exists():
|
||||
ar_out_target = float(settings.objects.filter(key='AR-Outbound%')[0].value)
|
||||
else:
|
||||
ar_out_target = 0.75
|
||||
channels.objects.all().update(ar_out_target=ar_out_target*100)
|
||||
except Exception as e:
|
||||
print('Migration step failed:', str(e))
|
||||
|
||||
def revert_defaults(apps, schedma_editor):
|
||||
pass
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('gui', '0017_autopilot'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name='channels',
|
||||
old_name='ar_target',
|
||||
new_name='ar_in_target',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='channels',
|
||||
name='ar_amt_target',
|
||||
field=models.BigIntegerField(default=100000),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='channels',
|
||||
name='ar_out_target',
|
||||
field=models.IntegerField(default=75),
|
||||
),
|
||||
migrations.RunPython(update_defaults, revert_defaults),
|
||||
]
|
||||
|
|
@ -79,7 +79,27 @@ class Channels(models.Model):
|
|||
is_active = models.BooleanField()
|
||||
is_open = models.BooleanField()
|
||||
auto_rebalance = models.BooleanField(default=False)
|
||||
ar_target = models.IntegerField(default=100)
|
||||
ar_amt_target = models.BigIntegerField(default=100000)
|
||||
ar_in_target = models.IntegerField(default=100)
|
||||
ar_out_target = models.IntegerField(default=75)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if not self.ar_out_target:
|
||||
if LocalSettings.objects.filter(key='AR-Outbound%').exists():
|
||||
outbound_setting = float(LocalSettings.objects.filter(key='AR-Outbound%')[0].value)
|
||||
else:
|
||||
LocalSettings(key='AR-Outbound%', value='0.75').save()
|
||||
outbound_setting = 0.75
|
||||
self.ar_out_target = int(outbound_setting * 100)
|
||||
if not self.ar_amt_target:
|
||||
if LocalSettings.objects.filter(key='AR-Target%').exists():
|
||||
amt_setting = float(LocalSettings.objects.filter(key='AR-Target%')[0].value)
|
||||
else:
|
||||
LocalSettings(key='AR-Target%', value='0.05').save()
|
||||
amt_setting = 0.05
|
||||
self.ar_amt_target = int(amt_setting * self.capacity)
|
||||
super(Channels, self).save(*args, **kwargs)
|
||||
|
||||
class Meta:
|
||||
app_label = 'gui'
|
||||
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ class ChannelSerializer(serializers.HyperlinkedModelSerializer):
|
|||
remote_fee_rate = serializers.ReadOnlyField()
|
||||
is_active = serializers.ReadOnlyField()
|
||||
is_open = serializers.ReadOnlyField()
|
||||
num_updates = serializers.ReadOnlyField()
|
||||
class Meta:
|
||||
model = Channels
|
||||
exclude = []
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %} {{ block.super }} - Actions{% endblock %}
|
||||
{% block content %}
|
||||
{% load humanize %}
|
||||
{% if action_list %}
|
||||
|
|
@ -24,8 +25,8 @@
|
|||
</tr>
|
||||
{% for channel in action_list %}
|
||||
<tr>
|
||||
<td>{{ channel.chan_id }}</a></td>
|
||||
<td>{{ channel.alias }}</a></td>
|
||||
<td title="{{ channel.funding_txid }}:{{ channel.output_index }}"><a href="{{ network_links }}/{{ network }}tx/{{ channel.funding_txid }}" target="_blank">{{ channel.chan_id }}</a></td>
|
||||
<td title="{{ channel.remote_pubkey }}"><a href="{{ graph_links }}/{{ network }}node/{{ channel.remote_pubkey }}" target="_blank">{{ channel.alias }}</a></td>
|
||||
<td>{{ channel.capacity|intcomma }}</td>
|
||||
<td>{{ channel.local_balance|intcomma }} ({{ channel.outbound_percent }}%)</td>
|
||||
<td><div class="w3-orange w3-round">{% if channel.inbound_percent == 0 %}<div class="w3-container w3-round w3-blue" style="height:16px;width:100%"></div>{% elif channel.outbound_percent == 0 %}<div class="w3-container w3-round w3-orange" style="height:16px;width:100%"></div>{% else %}<div class="w3-container w3-round w3-blue" style="height:16px;width:{{ channel.outbound_percent }}%"></div>{% endif %}</div></td>
|
||||
|
|
@ -38,19 +39,13 @@
|
|||
<td>{{ channel.remote_fee_rate|intcomma }}</td>
|
||||
<td>{{ channel.remote_base_fee|intcomma }}</td>
|
||||
<td>
|
||||
{% if channel.auto_rebalance == True %}
|
||||
<form action="/autorebalance/" method="post">
|
||||
<form action="/update_channel/" method="post">
|
||||
{% csrf_token %}
|
||||
<input type="submit" value="Disable">
|
||||
<input type="submit" value="{% if channel.auto_rebalance == True %}Disable{% else %}Enable{% endif %}">
|
||||
<input type="hidden" name="chan_id" value="{{ channel.chan_id }}">
|
||||
<input type="hidden" name="update_target" value="5">
|
||||
<input type="hidden" name="target" value="0">
|
||||
</form>
|
||||
{% else %}
|
||||
<form action="/autorebalance/" method="post">
|
||||
{% csrf_token %}
|
||||
<input type="submit" value="Enable">
|
||||
<input type="hidden" name="chan_id" value="{{ channel.chan_id }}">
|
||||
</form>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td title="{{ channel.reason }}">{{ channel.output }}</td>
|
||||
</tr>
|
||||
|
|
|
|||
131
gui/templates/advanced.html
Normal file
131
gui/templates/advanced.html
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %} {{ block.super }} - Advanced{% endblock %}
|
||||
{% block content %}
|
||||
{% load humanize %}
|
||||
{% if channels %}
|
||||
<div class="w3-container w3-padding-small">
|
||||
<h2>Advanced Channel Settings</h2>
|
||||
<div class="w3-container w3-padding-small" style="overflow-x: scroll">
|
||||
<table class="w3-table-all w3-centered w3-hoverable">
|
||||
<tr>
|
||||
<th>Channel ID</th>
|
||||
<th>Peer Alias</th>
|
||||
<th>Capacity</th>
|
||||
<th>Outbound Liquidity</th>
|
||||
<th width=10%></th>
|
||||
<th>Inbound Liquidity</th>
|
||||
<th>oRate</th>
|
||||
<th>oBase</th>
|
||||
<th>iRate</th>
|
||||
<th>iBase</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 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>Active</th>
|
||||
</tr>
|
||||
{% for channel in channels %}
|
||||
<tr>
|
||||
<td title="{{ channel.funding_txid }}:{{ channel.output_index }}"><a href="{{ network_links }}/{{ network }}tx/{{ channel.funding_txid }}" target="_blank">{{ channel.chan_id }}</a></td>
|
||||
<td title="{{ channel.remote_pubkey }}"><a href="{{ graph_links }}/{{ network }}node/{{ channel.remote_pubkey }}" target="_blank">{{ channel.alias }}</a></td>
|
||||
<td>{{ channel.capacity|intcomma }}</td>
|
||||
<td>{{ channel.local_balance|intcomma }} ({{ channel.out_percent }}%)</td>
|
||||
<td><div class="w3-orange w3-round">{% if channel.in_percent == 0 %}<div class="w3-container w3-round w3-blue" style="height:16px;width:100%"></div>{% elif channel.out_percent == 0 %}<div class="w3-container w3-round w3-orange" style="height:16px;width:100%"></div>{% else %}<div class="w3-container w3-round w3-blue" style="height:16px;width:{{ channel.out_percent }}%"></div>{% endif %}</div></td>
|
||||
<td>{{ channel.remote_balance|intcomma }} ({{ channel.in_percent }}%)</td>
|
||||
<td>
|
||||
<form action="/update_channel/" method="post">
|
||||
{% csrf_token %}
|
||||
<input style="text-align:center" id="target" type="number" min="0" max="100000" name="target" value="{{ channel.local_fee_rate }}">
|
||||
<input type="hidden" name="chan_id" value="{{ channel.chan_id }}">
|
||||
<input type="hidden" name="update_target" value="1">
|
||||
</form>
|
||||
</td>
|
||||
<td>
|
||||
<form action="/update_channel/" method="post">
|
||||
{% csrf_token %}
|
||||
<input style="text-align:center" id="target" type="number" min="0" max="100000" name="target" value="{{ channel.local_base_fee }}">
|
||||
<input type="hidden" name="chan_id" value="{{ channel.chan_id }}">
|
||||
<input type="hidden" name="update_target" value="0">
|
||||
</form>
|
||||
</td>
|
||||
<td>{{ channel.remote_fee_rate|intcomma }}</td>
|
||||
<td>{{ channel.remote_base_fee|intcomma }}</td>
|
||||
<td>
|
||||
<form action="/update_channel/" method="post">
|
||||
{% csrf_token %}
|
||||
<input style="text-align:center" id="target" type="number" min="0" max="100000000" name="target" value="{{ channel.ar_amt_target }}">
|
||||
<input type="hidden" name="chan_id" value="{{ channel.chan_id }}">
|
||||
<input type="hidden" name="update_target" value="2">
|
||||
</form>
|
||||
</td>
|
||||
<td {% if channel.auto_rebalance == False %}style="background-color: #80ced6"{% else %}style="background-color: #f18973"{% endif %}>
|
||||
<form action="/update_channel/" method="post">
|
||||
{% csrf_token %}
|
||||
<input style="text-align:center" id="target" type="number" min="0" max="100" name="target" value="{{ channel.ar_out_target }}">
|
||||
<input type="hidden" name="chan_id" value="{{ channel.chan_id }}">
|
||||
<input type="hidden" name="update_target" value="4">
|
||||
</form>
|
||||
</td>
|
||||
<td {% if channel.auto_rebalance == True %}style="background-color: #80ced6"{% else %}style="background-color: #f18973"{% endif %}>
|
||||
<form action="/update_channel/" method="post">
|
||||
{% csrf_token %}
|
||||
<input style="text-align:center" id="target" type="number" min="0" max="100" name="target" value="{{ channel.ar_in_target }}">
|
||||
<input type="hidden" name="chan_id" value="{{ channel.chan_id }}">
|
||||
<input type="hidden" name="update_target" value="3">
|
||||
</form>
|
||||
</td>
|
||||
<td>
|
||||
<form action="/update_channel/" method="post">
|
||||
{% csrf_token %}
|
||||
<input type="submit" value="{% if channel.auto_rebalance == True %}Disable{% else %}Enable{% endif %}">
|
||||
<input type="hidden" name="chan_id" value="{{ channel.chan_id }}">
|
||||
<input type="hidden" name="update_target" value="5">
|
||||
<input type="hidden" name="target" value="0">
|
||||
</form>
|
||||
</td>
|
||||
<td>{{ channel.is_active }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if not channels %}
|
||||
<div class="w3-container w3-padding-small">
|
||||
<center><h1>You dont have any channels to setup yet!</h1></center>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if local_settings %}
|
||||
<div class="w3-container w3-padding-small">
|
||||
<h2>Update Local Settings</h2>
|
||||
<table class="w3-table-all w3-centered w3-hoverable">
|
||||
<tr>
|
||||
<th>Key</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
{% for settings in local_settings %}
|
||||
<tr>
|
||||
<td>{{ settings.key }}</td>
|
||||
<td>
|
||||
<form action="/update_setting/" method="post">
|
||||
{% csrf_token %}
|
||||
{% if settings.key|slice:"-1:" == '%' %}
|
||||
<input style="text-align:center" id="value" type="number" step="0.01" min="0" max="1" name="value" value="{{ settings.value }}">
|
||||
{% elif settings.key == 'AR-Time' %}
|
||||
<input style="text-align:center" id="value" type="number" min="0" max="60" name="value" value="{{ settings.value }}">
|
||||
{% elif settings.key == 'AR-MaxFeeRate' %}
|
||||
<input style="text-align:center" id="value" type="number" min="0" max="2500" name="value" value="{{ settings.value }}">
|
||||
{% elif settings.key|slice:":3" == 'AR-' %}
|
||||
<input style="text-align:center" id="value" type="number" min="0" max="1" name="value" value="{{ settings.value }}">
|
||||
{% else %}
|
||||
<input style="text-align:center" id="value" type="text" name="value" value="{{ settings.value }}">
|
||||
{% endif %}
|
||||
<input type="hidden" name="key" value="{{ settings.key }}">
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %} {{ block.super }} - Autopilot{% endblock %}
|
||||
{% block content %}
|
||||
{% load humanize %}
|
||||
{% if autopilot %}
|
||||
|
|
@ -29,7 +30,7 @@
|
|||
{% if not autopilot %}
|
||||
<div class="w3-container w3-padding-small">
|
||||
<center><h1>No autopilot logs to see here yet!</h1></center>
|
||||
<center><h6>Experimental. Advanced users may activate via api.</h6></center>
|
||||
<center><h6>Experimental. This will allow LNDg to automatically act upon the suggestions found <a href="/suggested_actions" target="_blank">here</a>.</h6></center>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %} {{ block.super }} - Balances{% endblock %}
|
||||
{% block content %}
|
||||
{% load humanize %}
|
||||
{% if utxos %}
|
||||
|
|
@ -13,9 +14,9 @@
|
|||
</tr>
|
||||
{% for uxto in utxos %}
|
||||
<tr>
|
||||
<td>{{ uxto.address }}</td>
|
||||
<td><a href="{{ network_links }}/{{ network }}address/{{ uxto.address }}" target="_blank">{{ uxto.address }}</a></td>
|
||||
<td>{{ uxto.amount_sat|intcomma }}</td>
|
||||
<td>{{ uxto.outpoint.txid_str }}</td>
|
||||
<td><a href="{{ network_links }}/{{ network }}tx/{{ uxto.outpoint.txid_str }}" target="_blank">{{ uxto.outpoint.txid_str }}</a></td>
|
||||
<td>{{ uxto.confirmations|intcomma }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
|
@ -35,9 +36,9 @@
|
|||
</tr>
|
||||
{% for transaction in transactions %}
|
||||
<tr>
|
||||
<td>{{ transaction.tx_hash }}</td>
|
||||
<td><a href="{{ network_links }}/{{ network }}tx/{{ transaction.tx_hash }}" target="_blank">{{ transaction.tx_hash }}</a></td>
|
||||
<td>{{ transaction.amount }}</td>
|
||||
<td>{{ transaction.block_height }}</td>
|
||||
<td>{% if transaction.block_height == 0 %}{{ transaction.block_height }}{% else %}<a href="{{ network_links }}/{{ network }}block/{{ transaction.block_height }}" target="_blank">{{ transaction.block_height }}</a>{% endif %}</td>
|
||||
<td>{{ transaction.fee }}</td>
|
||||
<td>{{ transaction.label }}</td>
|
||||
</tr>
|
||||
|
|
@ -45,4 +46,9 @@
|
|||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if not utxos and not transactions %}
|
||||
<div class="w3-container w3-padding-small">
|
||||
<center><h1>No wallet transactions found!</h1></center>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
</style>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>LNDg</title>
|
||||
<title>{% block title %}LNDg{% endblock %}</title>
|
||||
{% load static %}
|
||||
{% load qr_code %}
|
||||
<link href="{% static 'w3style.css' %}" rel="stylesheet" type="text/css">
|
||||
|
|
@ -28,7 +28,7 @@
|
|||
<footer>
|
||||
<div id="footer">
|
||||
<div class="w3-container w3-padding-small">
|
||||
<center>LNDg v1.0.1</center>
|
||||
<center>LNDg v1.0.2</center>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
|
|
|||
65
gui/templates/channels.html
Normal file
65
gui/templates/channels.html
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %} {{ block.super }} - Channels{% endblock %}
|
||||
{% block content %}
|
||||
{% load humanize %}
|
||||
{% if channels %}
|
||||
<div class="w3-container w3-padding-small">
|
||||
<h2>Channel Performance</h2>
|
||||
<div class="w3-container w3-padding-small" style="overflow-x: scroll">
|
||||
<table class="w3-table-all w3-centered w3-hoverable">
|
||||
<tr>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th colspan="3">7-Day Activity And Revenue </th>
|
||||
<th colspan="3">30-Day Activity And Revenue </th>
|
||||
<th colspan="2">Local Fees</th>
|
||||
<th colspan="2">Peer Fees</th>
|
||||
<th colspan="2"> Channel Health</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th width=1%>Channel ID</th>
|
||||
<th>Peer Alias</th>
|
||||
<th width=1%>Capacity</th>
|
||||
<th title="Routed Out | Rebalanced In">Outbound Flow</th>
|
||||
<th title="Revenue Out [Net Profits] | Assisted Revenue In">Out [Profit] | In</th>
|
||||
<th title="Routed In | Rebalanced Out">Inbound Flow</th>
|
||||
<th title="Routed Out | Rebalanced In">Outbound Flow</th>
|
||||
<th title="Revenue Out [Net Profits] | Assisted Revenue In">Out [Profit] | In</th>
|
||||
<th title="Routed In | Rebalanced Out">Inbound Flow</th>
|
||||
<th width=1%>Rate</th>
|
||||
<th width=1%>Base</th>
|
||||
<th width=1%>Rate</th>
|
||||
<th width=1%>Base</th>
|
||||
<th title="The count of updates is directly correlated to the space the channel takes up in your channel database." width=1%>Updates</th>
|
||||
<th title="The opener of the channel usually pays the closing costs during a co-operative close." width=1%>Opener</th>
|
||||
</tr>
|
||||
{% for channel in channels %}
|
||||
<tr>
|
||||
<td title="{{ channel.funding_txid }}:{{ channel.output_index }}"><a href="{{ network_links }}/{{ network }}tx/{{ channel.funding_txid }}" target="_blank">{{ channel.chan_id }}</a></td>
|
||||
<td title="{{ channel.remote_pubkey }}"><a href="{{ graph_links }}/{{ network }}node/{{ channel.remote_pubkey }}" target="_blank">{{ channel.alias }}</a></td>
|
||||
<td>{{ channel.capacity }} M</td>
|
||||
<td>{{ channel.amt_routed_out_7day|intcomma }} M ({{ channel.routed_out_7day }}) | {{ channel.amt_rebal_in_7day|intcomma }} M ({{ channel.rebal_in_7day }})</td>
|
||||
<td>{{ channel.revenue_7day|intcomma }} [{{ channel.profits_7day|intcomma }}] | {{ channel.revenue_assist_7day|intcomma }}</td>
|
||||
<td>{{ channel.amt_routed_in_7day|intcomma }} M ({{ channel.routed_in_7day }}) | {{ channel.amt_rebal_out_7day|intcomma }} M ({{ channel.rebal_out_7day }})</td>
|
||||
<td>{{ channel.amt_routed_out_30day|intcomma }} M ({{ channel.routed_out_30day }}) | {{ channel.amt_rebal_in_30day|intcomma }} M ({{ channel.rebal_in_30day }})</td>
|
||||
<td>{{ channel.revenue_30day|intcomma }} [{{ channel.profits_30day|intcomma }}] | {{ channel.revenue_assist_30day|intcomma }}</td>
|
||||
<td>{{ channel.amt_routed_in_30day|intcomma }} M ({{ channel.routed_in_30day }}) | {{ channel.amt_rebal_out_30day|intcomma }} M ({{ channel.rebal_out_30day }})</td>
|
||||
<td>{{ channel.local_fee_rate|intcomma }}</td>
|
||||
<td>{{ channel.local_base_fee|intcomma }}</td>
|
||||
<td>{{ channel.remote_fee_rate|intcomma }}</td>
|
||||
<td>{{ channel.remote_base_fee|intcomma }}</td>
|
||||
<td>{{ channel.num_updates|intcomma }}</td>
|
||||
<td title="Block Opened: {{ channel.open_block }}">{{ channel.initiator }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if not channels %}
|
||||
<div class="w3-container w3-padding-small">
|
||||
<center><h1>You dont have any channels to analyze yet!</h1></center>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
41
gui/templates/failed_htlcs.html
Normal file
41
gui/templates/failed_htlcs.html
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %} {{ block.super }} - Failed HTLCs{% endblock %}
|
||||
{% block content %}
|
||||
{% load humanize %}
|
||||
{% if failed_htlcs %}
|
||||
<div class="w3-container w3-padding-small">
|
||||
<h2>Last 150 Failed HTLCs</h2>
|
||||
<table class="w3-table-all w3-centered w3-hoverable">
|
||||
<tr>
|
||||
<th>Timestamp</th>
|
||||
<th>Chan In ID</th>
|
||||
<th>Chan Out ID</th>
|
||||
<th>Chan In Alias</th>
|
||||
<th>Chan Out Alias</th>
|
||||
<th>Forward Amount</th>
|
||||
<th>Potential Fee</th>
|
||||
<th>HTLC Failure</th>
|
||||
<th>Failure Detail</th>
|
||||
</tr>
|
||||
{% for failed_htlc in failed_htlcs %}
|
||||
<tr>
|
||||
<td title="{{ failed_htlc.timestamp }}">{{ failed_htlc.timestamp|naturaltime }}</td>
|
||||
<td>{{ failed_htlc.chan_id_in }}</td>
|
||||
<td>{{ failed_htlc.chan_id_out }}</td>
|
||||
<td>{{ failed_htlc.chan_in_alias }}</td>
|
||||
<td>{{ failed_htlc.chan_out_alias }}</td>
|
||||
<td>{{ failed_htlc.amount|intcomma }}</td>
|
||||
<td>{{ failed_htlc.missed_fee|intcomma }}</td>
|
||||
<td>{% if failed_htlc.wire_failure == 15 %}Temporary Channel Failure{% elif failed_htlc.wire_failure == 18 %}Unknown Next Peer{% elif failed_htlc.wire_failure == 12 %}Fee Insufficient{% else %}{{ failed_htlc.wire_failure }}{% endif %}</td>
|
||||
<td>{% if failed_htlc.failure_detail == 1 %}---{% elif failed_htlc.failure_detail == 5 %}HTLC Exceeds Max{% elif failed_htlc.failure_detail == 6 %}Insufficient Balance{% elif failed_htlc.failure_detail == 20 %}Invalid Keysend{% elif failed_htlc.failure_detail == 22 %}Circular Route{% else %}{{ failed_htlc.failure_detail }}{% endif %}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if not failed_htlcs %}
|
||||
<div class="w3-container w3-padding-small">
|
||||
<center><h1>You dont have any failed HTLCs yet.</h1></center>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
41
gui/templates/forwards.html
Normal file
41
gui/templates/forwards.html
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %} {{ block.super }} - Forwards{% endblock %}
|
||||
{% block content %}
|
||||
{% load humanize %}
|
||||
{% if forwards %}
|
||||
<div class="w3-container w3-padding-small">
|
||||
<h2>Last 150 Forwards</h2>
|
||||
<table class="w3-table-all w3-centered w3-hoverable">
|
||||
<tr>
|
||||
<th>Timestamp</th>
|
||||
<th>Amount In</th>
|
||||
<th>Amount Out</th>
|
||||
<th>Channel In Alias</th>
|
||||
<th>Channel Out Alias</th>
|
||||
<th>Channel In Id</th>
|
||||
<th>Channel Out Id</th>
|
||||
<th>Fees Earned</th>
|
||||
<th>PPM Earned</th>
|
||||
</tr>
|
||||
{% for forward in forwards %}
|
||||
<tr>
|
||||
<td title="{{ forward.forward_date }}">{{ forward.forward_date|naturaltime }}</td>
|
||||
<td>{{ forward.amt_in|intcomma }}</td>
|
||||
<td>{{ forward.amt_out|intcomma }}</td>
|
||||
<td>{{ forward.chan_in_alias }}</td>
|
||||
<td>{{ forward.chan_out_alias }}</td>
|
||||
<td>{{ forward.chan_id_in }}</td>
|
||||
<td>{{ forward.chan_id_out }}</td>
|
||||
<td>{{ forward.fee }}</td>
|
||||
<td>{{ forward.ppm|intcomma }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if not forwards %}
|
||||
<div class="w3-container w3-padding-small">
|
||||
<center><h1>You dont have any forwards yet.</h1></center>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %} {{ block.super }} - Dashboard{% endblock %}
|
||||
{% block content %}
|
||||
{% load humanize %}
|
||||
<div class="w3-container w3-padding-small">
|
||||
<h3><a href="https://1ml.com/{{ network }}node/{{ node_info.identity_pubkey }}" target="_blank">{{ node_info.alias }}</a> | {{ node_info.identity_pubkey }}</h3>
|
||||
<h3><a href="{{ graph_links }}/{{ network }}node/{{ node_info.identity_pubkey }}" target="_blank">{{ node_info.alias }}</a> | {{ node_info.identity_pubkey }}</h3>
|
||||
<h4>Capacity: {{ capacity|intcomma }} | Active Channels: {{ node_info.num_active_channels }} / {{ total_channels }} | <a href="/peers" target="_blank">Peers</a>: {{ node_info.num_peers }} | DB Size: {{ db_size }} GB</h4>
|
||||
<h4>Public Address: {% for info in node_info.uris %}{{ info }} | {% endfor %}</h4>
|
||||
<h4>Lnd sync: {{ node_info.synced_to_graph }} | chain sync: {{ node_info.synced_to_chain }} | {% for info in node_info.chains %}{{ info }}{% endfor %} | {{ node_info.block_height }} | {{ node_info.block_hash }}</h4>
|
||||
|
|
@ -23,7 +24,7 @@
|
|||
<div class="w3-container w3-padding-small">
|
||||
<h4>Inbound Liquidity: {{ inbound|intcomma }} | Outbound Liquidity: {{ outbound|intcomma }} | Liquidity Ratio: {{ liq_ratio }}%</h4>
|
||||
<h4>Balance In Limbo: {{ limbo_balance|intcomma }} | Unsettled Liquidity: {{ unsettled|intcomma }} | <a href="/pending_htlcs" target="_blank">Pending HTLCs</a>: {{ pending_htlc_count }}</h4>
|
||||
<h4><a href="/suggested_opens" target="_blank">Suggested New Peers</a> | <a href="/suggested_actions" target="_blank">Suggested AR Actions</a> | <a href="/autopilot" target="_blank">Autopilot Logs</a></h4>
|
||||
<h4><a href="/suggested_opens" target="_blank">Suggested New Peers</a> | <a href="/suggested_actions" target="_blank">Suggested AR Actions</a> | <a href="/autopilot" target="_blank">Autopilot Logs</a> | <a href="/channels" target="_blank">Channel Performance</a> | <a href="/advanced" target="_blank">Advanced Settings</a></h4>
|
||||
</div>
|
||||
{% if active_channels %}
|
||||
<div class="w3-container w3-padding-small">
|
||||
|
|
@ -46,13 +47,13 @@
|
|||
<th>iLife</th>
|
||||
<th>iRate</th>
|
||||
<th>iBase</th>
|
||||
<th width=4%>iTarget%</th>
|
||||
<th width=4%>AR</th>
|
||||
<th title="When AR is ENABLED for the channel, keep pulling IN to the channel until its inbound liquidity falls below the iTarget%." width=4%>iTarget%</th>
|
||||
<th title="When AR is ENABLED it will refill the channel with outbound liquidity." width=4%>AR</th>
|
||||
</tr>
|
||||
{% for channel in active_channels %}
|
||||
<tr>
|
||||
<td title="{{ channel.funding_txid }}:{{ channel.output_index }}"><a href="https://mempool.space/{{ network }}tx/{{ channel.funding_txid }}" target="_blank">{{ channel.chan_id }}</a></td>
|
||||
<td title="{{ channel.remote_pubkey }}"><a href="https://1ml.com/{{ network }}node/{{ channel.remote_pubkey }}" target="_blank">{{ channel.alias }}</a></td>
|
||||
<td title="{{ channel.funding_txid }}:{{ channel.output_index }}"><a href="{{ network_links }}/{{ network }}tx/{{ channel.funding_txid }}" target="_blank">{{ channel.chan_id }}</a></td>
|
||||
<td title="{{ channel.remote_pubkey }}"><a href="{{ graph_links }}/{{ network }}node/{{ channel.remote_pubkey }}" target="_blank">{{ channel.alias }}</a></td>
|
||||
<td>{{ channel.capacity|intcomma }}</td>
|
||||
<td>{{ channel.local_balance|intcomma }} ({{ channel.outbound_percent }}%)</td>
|
||||
<td><div class="w3-orange w3-round">{% if channel.inbound_percent == 0 %}<div class="w3-container w3-round w3-blue" style="height:16px;width:100%"></div>{% elif channel.outbound_percent == 0 %}<div class="w3-container w3-round w3-orange" style="height:16px;width:100%"></div>{% else %}<div class="w3-container w3-round w3-blue" style="height:16px;width:{{ channel.outbound_percent }}%"></div>{% endif %}</div></td>
|
||||
|
|
@ -68,29 +69,24 @@
|
|||
<td>{{ channel.remote_base_fee|intcomma }}</td>
|
||||
<td>
|
||||
{% if channel.auto_rebalance == True %}
|
||||
<form action="/ar_target/" method="post">
|
||||
<form action="/update_channel/" method="post">
|
||||
{% csrf_token %}
|
||||
<input style="text-align:center" id="ar_target" type="number" min="0" max="100" name="ar_target" value="{{ channel.ar_target }}">
|
||||
<input style="text-align:center" id="target" type="number" min="0" max="100" name="target" value="{{ channel.ar_in_target }}">
|
||||
<input type="hidden" name="chan_id" value="{{ channel.chan_id }}">
|
||||
<input type="hidden" name="update_target" value="3">
|
||||
</form>
|
||||
{% else %}
|
||||
---
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if channel.auto_rebalance == True %}
|
||||
<form action="/autorebalance/" method="post">
|
||||
{% csrf_token %}
|
||||
<input type="submit" value="Disable">
|
||||
<input type="hidden" name="chan_id" value="{{ channel.chan_id }}">
|
||||
</form>
|
||||
{% else %}
|
||||
<form action="/autorebalance/" method="post">
|
||||
{% csrf_token %}
|
||||
<input type="submit" value="Enable">
|
||||
<input type="hidden" name="chan_id" value="{{ channel.chan_id }}">
|
||||
</form>
|
||||
{% endif %}
|
||||
<form action="/update_channel/" method="post">
|
||||
{% csrf_token %}
|
||||
<input type="submit" value="{% if channel.auto_rebalance == True %}Disable{% else %}Enable{% endif %}">
|
||||
<input type="hidden" name="chan_id" value="{{ channel.chan_id }}">
|
||||
<input type="hidden" name="update_target" value="5">
|
||||
<input type="hidden" name="target" value="0">
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
|
@ -122,8 +118,8 @@
|
|||
</tr>
|
||||
{% for channel in inactive_channels %}
|
||||
<tr>
|
||||
<td title="{{ channel.funding_txid }}:{{ channel.output_index }}"><a href="https://mempool.space/{{ network }}tx/{{ channel.funding_txid }}" target="_blank">{{ channel.chan_id }}</a></td>
|
||||
<td title="{{ channel.remote_pubkey }}"><a href="https://1ml.com/{{ network }}node/{{ channel.remote_pubkey }}" target="_blank">{{ channel.alias }}</a></td>
|
||||
<td title="{{ channel.funding_txid }}:{{ channel.output_index }}"><a href="{{ network_links }}/{{ network }}tx/{{ channel.funding_txid }}" target="_blank">{{ channel.chan_id }}</a></td>
|
||||
<td title="{{ channel.remote_pubkey }}"><a href="{{ graph_links }}/{{ network }}node/{{ channel.remote_pubkey }}" target="_blank">{{ channel.alias }}</a></td>
|
||||
<td>{{ channel.capacity|intcomma }}</td>
|
||||
<td>{{ channel.local_balance|intcomma }}</td>
|
||||
<td><div class="w3-orange w3-round">{% if channel.inbound_percent == 0 %}<div class="w3-container w3-round w3-blue" style="height:16px;width:100%"></div>{% elif channel.outbound_percent == 0 %}<div class="w3-container w3-round w3-orange" style="height:16px;width:100%"></div>{% else %}<div class="w3-container w3-round w3-blue" style="height:16px;width:{{ channel.outbound_percent }}%"></div>{% endif %}</div></td>
|
||||
|
|
@ -138,29 +134,24 @@
|
|||
<td>{{ channel.initiator }}</td>
|
||||
<td>
|
||||
{% if channel.auto_rebalance == True %}
|
||||
<form action="/ar_target/" method="post">
|
||||
<form action="/update_channel/" method="post">
|
||||
{% csrf_token %}
|
||||
<input style="text-align:center" id="ar_target" type="number" min="0" max="100" name="ar_target" value="{{ channel.ar_target }}">
|
||||
<input style="text-align:center" id="target" type="number" min="0" max="100" name="target" value="{{ channel.ar_in_target }}">
|
||||
<input type="hidden" name="chan_id" value="{{ channel.chan_id }}">
|
||||
<input type="hidden" name="update_target" value="3">
|
||||
</form>
|
||||
{% else %}
|
||||
---
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if channel.auto_rebalance == True %}
|
||||
<form action="/autorebalance/" method="post">
|
||||
{% csrf_token %}
|
||||
<input type="submit" value="Disable">
|
||||
<input type="hidden" name="chan_id" value="{{ channel.chan_id }}">
|
||||
</form>
|
||||
{% else %}
|
||||
<form action="/autorebalance/" method="post">
|
||||
{% csrf_token %}
|
||||
<input type="submit" value="Enable">
|
||||
<input type="hidden" name="chan_id" value="{{ channel.chan_id }}">
|
||||
</form>
|
||||
{% endif %}
|
||||
<form action="/update_channel/" method="post">
|
||||
{% csrf_token %}
|
||||
<input type="submit" value="{% if channel.auto_rebalance == True %}Disable{% else %}Enable{% endif %}">
|
||||
<input type="hidden" name="chan_id" value="{{ channel.chan_id }}">
|
||||
<input type="hidden" name="update_target" value="5">
|
||||
<input type="hidden" name="target" value="0">
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
|
@ -182,9 +173,9 @@
|
|||
{% for channel in pending_open %}
|
||||
<tr>
|
||||
{% with pending_funding_txid=channel.channel.channel_point|slice:":-2" %}
|
||||
<td><a href='https://mempool.space/{{ network }}tx/{{ pending_funding_txid }}' target="_blank">{{ channel.channel.channel_point }}</a></td>
|
||||
<td><a href='{{ network_links }}/{{ network }}tx/{{ pending_funding_txid }}' target="_blank">{{ channel.channel.channel_point }}</a></td>
|
||||
{% endwith %}
|
||||
<td><a href="https://1ml.com/{{ network }}node/{{ channel.channel.remote_node_pub }}" target="_blank">{{ channel.channel.remote_node_pub }}</a></td>
|
||||
<td><a href="{{ graph_links }}/{{ network }}node/{{ channel.channel.remote_node_pub }}" target="_blank">{{ channel.channel.remote_node_pub }}</a></td>
|
||||
<td>{{ channel.channel.capacity|intcomma }}</td>
|
||||
<td>{{ channel.channel.local_balance|intcomma }}</td>
|
||||
<td>{{ channel.channel.remote_balance|intcomma }}</td>
|
||||
|
|
@ -210,9 +201,9 @@
|
|||
{% for channel in pending_closed %}
|
||||
<tr>
|
||||
{% with pending_funding_txid=channel.channel.channel_point|slice:":-2" %}
|
||||
<td><a href='https://mempool.space/{{ network }}tx/{{ pending_funding_txid }}' target="_blank">{{ channel.channel.channel_point }}</a></td>
|
||||
<td><a href='{{ network_links }}/{{ network }}tx/{{ pending_funding_txid }}' target="_blank">{{ channel.channel.channel_point }}</a></td>
|
||||
{% endwith %}
|
||||
<td><a href="https://1ml.com/{{ network }}node/{{ channel.channel.remote_node_pub }}" target="_blank">{{ channel.channel.remote_node_pub }}</a></td>
|
||||
<td><a href="{{ graph_links }}/{{ network }}node/{{ channel.channel.remote_node_pub }}" target="_blank">{{ channel.channel.remote_node_pub }}</a></td>
|
||||
<td>{{ channel.channel.capacity|intcomma }}</td>
|
||||
<td>{{ channel.channel.local_balance|intcomma }}</td>
|
||||
<td>{{ channel.channel.remote_balance|intcomma }}</td>
|
||||
|
|
@ -240,15 +231,15 @@
|
|||
{% for channel in pending_force_closed %}
|
||||
<tr>
|
||||
{% with pending_funding_txid=channel.channel.channel_point|slice:":-2" %}
|
||||
<td><a href='https://mempool.space/{{ network }}tx/{{ pending_funding_txid }}' target="_blank">{{ channel.channel.channel_point }}</a></td>
|
||||
<td><a href='{{ network_links }}/{{ network }}tx/{{ pending_funding_txid }}' target="_blank">{{ channel.channel.channel_point }}</a></td>
|
||||
{% endwith %}
|
||||
<td><a href="https://1ml.com/{{ network }}node/{{ channel.channel.remote_node_pub }}" target="_blank">{{ channel.channel.remote_node_pub }}</a></td>
|
||||
<td><a href="{{ graph_links }}/{{ network }}node/{{ channel.channel.remote_node_pub }}" target="_blank">{{ channel.channel.remote_node_pub }}</a></td>
|
||||
<td>{{ channel.channel.capacity|intcomma }}</td>
|
||||
<td>{{ channel.channel.local_balance|intcomma }}</td>
|
||||
<td>{{ channel.channel.remote_balance|intcomma }}</td>
|
||||
<td>{{ channel.limbo_balance|intcomma }}</td>
|
||||
<td>{{ channel.blocks_til_maturity|intcomma }}</td>
|
||||
<td><a href='https://mempool.space/{{ network }}tx/{{ channel.closing_txid }}' target="_blank">{{ channel.closing_txid }}</a></td>
|
||||
<td><a href='{{ network_links }}/{{ network }}tx/{{ channel.closing_txid }}' target="_blank">{{ channel.closing_txid }}</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
|
@ -270,9 +261,9 @@
|
|||
{% for channel in waiting_for_close %}
|
||||
<tr>
|
||||
{% with pending_funding_txid=channel.channel.channel_point|slice:":-2" %}
|
||||
<td><a href='https://mempool.space/{{ network }}tx/{{ pending_funding_txid }}' target="_blank">{{ channel.channel.channel_point }}</a></td>
|
||||
<td><a href='{{ network_links }}/{{ network }}tx/{{ pending_funding_txid }}' target="_blank">{{ channel.channel.channel_point }}</a></td>
|
||||
{% endwith %}
|
||||
<td><a href="https://1ml.com/{{ network }}node/{{ channel.channel.remote_node_pub }}" target="_blank">{{ channel.channel.remote_node_pub }}</a></td>
|
||||
<td><a href="{{ graph_links }}/{{ network }}node/{{ channel.channel.remote_node_pub }}" target="_blank">{{ channel.channel.remote_node_pub }}</a></td>
|
||||
<td>{{ channel.channel.capacity|intcomma }}</td>
|
||||
<td>{{ channel.channel.local_balance|intcomma }}</td>
|
||||
<td>{{ channel.channel.remote_balance|intcomma }}</td>
|
||||
|
|
@ -285,7 +276,7 @@
|
|||
{% endif %}
|
||||
{% if forwards %}
|
||||
<div class="w3-container w3-padding-small">
|
||||
<h2>Last 10 Payments Routed</h2>
|
||||
<h2>Last 10 <a href="/forwards" target="_blank">Payments Routed</a></h2>
|
||||
<table class="w3-table-all w3-centered w3-hoverable">
|
||||
<tr>
|
||||
<th>Timestamp</th>
|
||||
|
|
@ -337,7 +328,7 @@
|
|||
<td>{{ rebalance.value|intcomma }}</td>
|
||||
<td>{{ rebalance.fee_limit|intcomma }}</td>
|
||||
<td>{% if rebalance.target_alias == '' %}None Specified{% else %}{{ rebalance.target_alias }}{% endif %}</td>
|
||||
<td>{% 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 %}</td>
|
||||
<td title="{{ rebalance.status }}">{% 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 %}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
|
@ -345,7 +336,7 @@
|
|||
{% endif %}
|
||||
{% if payments %}
|
||||
<div class="w3-container w3-padding-small">
|
||||
<h2>Last 5 Payments Sent</h2>
|
||||
<h2>Last 5 <a href="/payments" target="_blank">Payments Sent</a></h2>
|
||||
<table class="w3-table-all w3-centered w3-hoverable">
|
||||
<tr>
|
||||
<th>Timestamp</th>
|
||||
|
|
@ -376,7 +367,7 @@
|
|||
{% endif %}
|
||||
{% if invoices %}
|
||||
<div class="w3-container w3-padding-small">
|
||||
<h2>Last 5 Payments Received</h2>
|
||||
<h2>Last 5 <a href="/invoices" target="_blank">Payments Received</a></h2>
|
||||
<table class="w3-table-all w3-centered w3-hoverable">
|
||||
<tr>
|
||||
<th>Created</th>
|
||||
|
|
@ -407,7 +398,7 @@
|
|||
{% endif %}
|
||||
{% if failed_htlcs %}
|
||||
<div class="w3-container w3-padding-small">
|
||||
<h2>Last 10 Failed HTLCs</h2>
|
||||
<h2>Last 10 <a href="/failed_htlcs" target="_blank">Failed HTLCs</a></h2>
|
||||
<table class="w3-table-all w3-centered w3-hoverable">
|
||||
<tr>
|
||||
<th>Timestamp</th>
|
||||
|
|
@ -467,9 +458,11 @@
|
|||
<label for="outbound_percent">Target Outbound Above (%): </label>
|
||||
<input id="outbound_percent" type="number" step="0.01" min="0" max="1" name="outbound_percent">
|
||||
<label for="fee_rate">Global Max Fee Rate (ppm): </label>
|
||||
<input id="fee_rate" type="number" min="0" max="1500" name="fee_rate">
|
||||
<input id="fee_rate" type="number" min="0" max="2500" name="fee_rate">
|
||||
<label for="max_cost">Max Cost (%): </label>
|
||||
<input id="max_cost" type="number" step="0.01" min="0" max="1" name="max_cost">
|
||||
<label for="autopilot">Autopilot: </label>
|
||||
<input id="autopilot" type="number" min="0" max="1" name="autopilot">
|
||||
<input type="submit" value="OK">
|
||||
</form>
|
||||
</div>
|
||||
|
|
@ -581,4 +574,4 @@
|
|||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
|
|
|||
41
gui/templates/invoices.html
Normal file
41
gui/templates/invoices.html
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %} {{ block.super }} - Invoices{% endblock %}
|
||||
{% block content %}
|
||||
{% load humanize %}
|
||||
{% if invoices %}
|
||||
<div class="w3-container w3-padding-small">
|
||||
<h2>Last 150 Invoices</h2>
|
||||
<table class="w3-table-all w3-centered w3-hoverable">
|
||||
<tr>
|
||||
<th>Created</th>
|
||||
<th>Settled</th>
|
||||
<th width=25%>Payment Hash</th>
|
||||
<th>Value</th>
|
||||
<th>Amount Paid</th>
|
||||
<th>State</th>
|
||||
<th>Channel In Alias</th>
|
||||
<th width=10%>Channel In</th>
|
||||
<th><a href="/keysends" target="_blank">Keysend</a></th>
|
||||
</tr>
|
||||
{% for invoice in invoices %}
|
||||
<tr>
|
||||
<td title="{{ invoice.creation_date }}">{{ invoice.creation_date|naturaltime }}</td>
|
||||
<td title="{{ invoice.settle_date }}">{% if invoice.state == 1 %}{{ invoice.settle_date|naturaltime }}{% else %}N/A{% endif %}</td>
|
||||
<td>{{ invoice.r_hash }}</td>
|
||||
<td>{{ invoice.value|add:"0"|intcomma }}</td>
|
||||
<td>{% if invoice.state == 1 %}{{ invoice.amt_paid|intcomma }}{% else %}N/A{% endif %}</td>
|
||||
<td>{% if invoice.state == 0 %}Open{% elif invoice.state == 1 %}Settled{% elif invoice.state == 2 %}Canceled{% else %}{{ invoice.state }}{% endif %}</td>
|
||||
<td>{{ invoice.chan_in_alias }}</td>
|
||||
<td>{{ invoice.chan_in }}</td>
|
||||
<td title="{{ invoice.message }}">{% if invoice.keysend_preimage != None %}Yes{% else %}No{% endif %}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if not invoices %}
|
||||
<div class="w3-container w3-padding-small">
|
||||
<center><h1>You dont have any invoices yet.</h1></center>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %} {{ block.super }} - Keysends{% endblock %}
|
||||
{% block content %}
|
||||
{% load humanize %}
|
||||
{% if keysends %}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %} {{ block.super }} - Opens{% endblock %}
|
||||
{% block content %}
|
||||
{% load humanize %}
|
||||
{% if open_list %}
|
||||
|
|
@ -11,13 +12,13 @@
|
|||
<th width=15%>Successful Payments Routed</th>
|
||||
<th width=12%>Amount Routed</th>
|
||||
<th width=12%>Fees Paid</th>
|
||||
<th width=12%>Effective PPM</th>
|
||||
<th width=12%>Volume Score</th>
|
||||
<th width=12%>Savings By Volume</th>
|
||||
<th title="Total fees paid to the peer over total routed through the peer." width=12%>Effective PPM</th>
|
||||
<th title="A score factoring in both number of payments and volume of payments routed through the peer." width=12%>Volume Score</th>
|
||||
<th title="The amount you could have potentially saved on rebalancing payments if you had been peered directly with this node over total volume routed through the peer." width=12%>Savings By Volume</th>
|
||||
</tr>
|
||||
{% for node in open_list %}
|
||||
<tr>
|
||||
<td>{{ node.node_pubkey }}</td>
|
||||
<td><a href="{{ graph_links }}/{{ network }}node/{{ node.node_pubkey }}" target="_blank">{{ node.node_pubkey }}</a></td>
|
||||
<td>{{ node.alias }}</td>
|
||||
<td>{{ node.count }}</td>
|
||||
<td>{{ node.amount|add:"0"|intcomma }}</td>
|
||||
|
|
@ -30,4 +31,9 @@
|
|||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if not open_list %}
|
||||
<div class="w3-container w3-padding-small">
|
||||
<center><h1>No potential peers can be calculated yet, try waiting until you have some payment data.</h1></center>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
41
gui/templates/payments.html
Normal file
41
gui/templates/payments.html
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %} {{ block.super }} - Payments{% endblock %}
|
||||
{% block content %}
|
||||
{% load humanize %}
|
||||
{% if payments %}
|
||||
<div class="w3-container w3-padding-small">
|
||||
<h2>Last 150 Payments</h2>
|
||||
<table class="w3-table-all w3-centered w3-hoverable">
|
||||
<tr>
|
||||
<th>Timestamp</th>
|
||||
<th width=28%>Hash</th>
|
||||
<th width=7%>Value</th>
|
||||
<th width=7%>Fee Paid</th>
|
||||
<th width=7%>Status</th>
|
||||
<th width=12%>Chan Out Alias</th>
|
||||
<th width=12%>Chan Out ID</th>
|
||||
<th width=5%>Route</th>
|
||||
<th width=8%>Keysend</th>
|
||||
</tr>
|
||||
{% for payment in payments %}
|
||||
<tr>
|
||||
<td title="{{ payment.creation_date }}">{{ payment.creation_date|naturaltime }}</td>
|
||||
<td>{{ payment.payment_hash }}</td>
|
||||
<td>{{ payment.value|add:"0"|intcomma }}</td>
|
||||
<td>{{ payment.fee|intcomma }}</td>
|
||||
<td>{% if payment.status == 1 %}In-Flight{% elif payment.status == 2 %}Succeeded{% elif payment.status == 3 %}Failed{% else %}{{ payment.status }}{% endif %}</td>
|
||||
<td>{% if payment.status == 2 %}{{ payment.chan_out_alias }}{% else %}N/A{% endif %}</td>
|
||||
<td>{% if payment.status == 2 %}{{ payment.chan_out }}{% else %}N/A{% endif %}</td>
|
||||
<td>{% if payment.status == 2 %}<a href="/route?={{ payment.payment_hash }}" target="_blank">Open</a>{% else %}N/A{% endif %}</td>
|
||||
<td title="{{ payment.message }}">{% if payment.keysend_preimage != None %}Yes{% else %}No{% endif %}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if not payments %}
|
||||
<div class="w3-container w3-padding-small">
|
||||
<center><h1>You dont have any payments yet.</h1></center>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %} {{ block.super }} - Peers{% endblock %}
|
||||
{% block content %}
|
||||
{% load humanize %}
|
||||
{% if peers %}
|
||||
|
|
@ -14,7 +15,7 @@
|
|||
</tr>
|
||||
{% for peer in peers %}
|
||||
<tr>
|
||||
<td><a href="https://1ml.com/node/{{ peer.pubkey }}" target="_blank">{{ peer.pubkey }}</a></td>
|
||||
<td><a href="{{ graph_links }}/{{ network }}node/{{ peer.pubkey }}" target="_blank">{{ peer.pubkey }}</a></td>
|
||||
<td>{{ peer.address }}</td>
|
||||
<td>{{ peer.inbound }}</td>
|
||||
<td>{{ peer.sat_sent|intcomma }}</td>
|
||||
|
|
@ -24,4 +25,9 @@
|
|||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if not peers %}
|
||||
<div class="w3-container w3-padding-small">
|
||||
<center><h1>No connected peers found!</h1></center>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %} {{ block.super }} - HTLCs{% endblock %}
|
||||
{% block content %}
|
||||
{% load humanize %}
|
||||
{% if outgoing_htlcs %}
|
||||
|
|
@ -55,4 +56,9 @@
|
|||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if not outgoing_htlcs and not incoming_htlcs %}
|
||||
<div class="w3-container w3-padding-small">
|
||||
<center><h1>No pending HTLCs were found!</h1></center>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %} {{ block.super }} - Routes{% endblock %}
|
||||
{% block content %}
|
||||
{% load humanize %}
|
||||
{% if route %}
|
||||
<div class="w3-container w3-padding-small">
|
||||
<h2>Route For Payment: {{ payment_hash }}</h2>
|
||||
<table class="w3-table-all w3-centered w3-hoverable">
|
||||
|
|
@ -8,22 +10,30 @@
|
|||
<th>Step</th>
|
||||
<th>Amount</th>
|
||||
<th>Fee</th>
|
||||
<th title="The fee in PPM paid on this hop.">PPM</th>
|
||||
<th title="The cost to get the payment to this hop of the payment.">Cost To</th>
|
||||
<th>Alias</th>
|
||||
<th>Channel ID</th>
|
||||
<th>Channel Capacity</th>
|
||||
<th>Cost To</th>
|
||||
</tr>
|
||||
{% for hop in route %}
|
||||
<tr>
|
||||
<td>{{ hop.step }}</td>
|
||||
<td>{{ hop.amt }}</td>
|
||||
<td>{{ hop.fee }}</td>
|
||||
<td>{{ hop.ppm }}</td>
|
||||
<td>{{ hop.cost_to }}</td>
|
||||
<td>{{ hop.alias }}</td>
|
||||
<td>{{ hop.chan_id }}</td>
|
||||
<td>{{ hop.chan_capacity|intcomma }}</td>
|
||||
<td>{{ hop.cost_to }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if not route %}
|
||||
<div class="w3-container w3-padding-small">
|
||||
<center><h1>A route was not found for this payment hash!</h1></center>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
|
@ -22,6 +22,10 @@ urlpatterns = [
|
|||
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 @@ urlpatterns = [
|
|||
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'),
|
||||
|
|
|
|||
410
gui/views.py
410
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 datetime import datetime, timedelta
|
|||
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 import lightning_pb2_grpc as lnrpc
|
|||
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 @@ class RebalancerViewSet(viewsets.ReadOnlyModelViewSet):
|
|||
serializer.save()
|
||||
return Response(serializer.data)
|
||||
else:
|
||||
print(serializer.errors)
|
||||
return Response(serializer.errors)
|
||||
|
||||
@api_view(['POST'])
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -3,4 +3,5 @@ djangorestframework
|
|||
django-qr-code
|
||||
grpcio
|
||||
protobuf
|
||||
pytz
|
||||
pytz
|
||||
pandas
|
||||
|
|
@ -93,6 +93,8 @@ User=<run_as_user>
|
|||
Group=<run_as_user>
|
||||
ExecStart=/usr/bin/bash /home/<run_as_user>/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`
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue