Skip to content

Commit

Permalink
v1.0.2 (#27)
Browse files Browse the repository at this point in the history
  • Loading branch information
cryptosharks131 authored Jan 21, 2022
1 parent b3c28c9 commit ea83dd3
Show file tree
Hide file tree
Showing 27 changed files with 979 additions and 164 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ db.sqlite3
lndg/settings.py
frontend
node_modules
lndg-admin.txt
lndg-admin.txt
.vscode
43 changes: 30 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,24 +127,40 @@ 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
### API Backend
The following data can be accessed at the /api endpoint:
`payments` `paymenthops` `invoices` `forwards` `onchain` `peers` `channels` `rebalancer` `settings` `pendinghtlcs` `failedhtlcs`
### 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

### Peer Reconnection
LNDg will automatically try to resolve any channels that are seen as inactive, no more than every 3 minutes per 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

### Suggests New Peers
LNDg will make suggestions for new peers to open channels to based on your node's successful routing history.
### 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.

### Auto-Rebalancer
Here are some notes to help you get started using the Auto-Rebalancer (AR).
### API Backend
The following data can be accessed at the /api endpoint:
`payments` `paymenthops` `invoices` `forwards` `onchain` `peers` `channels` `rebalancer` `settings` `pendinghtlcs` `failedhtlcs`

### Peer Reconnection
LNDg will automatically try to resolve any channels that are seen as inactive, no more than every 3 minutes per peer.

## 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.

Expand Down Expand Up @@ -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.

Expand All @@ -197,7 +213,8 @@ If you want a channel not to be picked for rebalancing (i.e. it is already full
![image](https://user-images.githubusercontent.com/38626122/148699286-0b1d2c13-191a-4c6c-99ae-ce3d8b8ac64d.png)
![image](https://user-images.githubusercontent.com/38626122/137809583-db743233-25c1-4d3e-aaec-2a7767de2c9f.png)

### 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
![image](https://user-images.githubusercontent.com/38626122/150556928-bb8772fb-14c4-4b7a-865e-a8350aac7f83.png)
![image](https://user-images.githubusercontent.com/38626122/137809809-1ed40cfb-9d12-447a-8e5e-82ae79605895.png)
![image](https://user-images.githubusercontent.com/38626122/137810021-4f69dcb0-5fce-4062-bc49-e75f5dd0feda.png)
![image](https://user-images.githubusercontent.com/38626122/137809882-4a87f86d-290c-456e-9606-ed669fd98561.png)
Expand Down
19 changes: 17 additions & 2 deletions gui/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 53 additions & 0 deletions gui/migrations/0018_auto_20220114_2218.py
Original file line number Diff line number Diff line change
@@ -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),
]
22 changes: 21 additions & 1 deletion gui/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand Down
1 change: 1 addition & 0 deletions gui/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = []
Expand Down
19 changes: 7 additions & 12 deletions gui/templates/action_list.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{% extends "base.html" %}
{% block title %} {{ block.super }} - Actions{% endblock %}
{% block content %}
{% load humanize %}
{% if action_list %}
Expand All @@ -24,8 +25,8 @@ <h2>Suggested Action List</h2>
</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>
Expand All @@ -38,19 +39,13 @@ <h2>Suggested Action List</h2>
<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>
Expand Down
131 changes: 131 additions & 0 deletions gui/templates/advanced.html
Original file line number Diff line number Diff line change
@@ -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 %}
3 changes: 2 additions & 1 deletion gui/templates/autopilot.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{% extends "base.html" %}
{% block title %} {{ block.super }} - Autopilot{% endblock %}
{% block content %}
{% load humanize %}
{% if autopilot %}
Expand Down Expand Up @@ -29,7 +30,7 @@ <h2>Autopilot Logs</h2>
{% 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 %}
Loading

0 comments on commit ea83dd3

Please sign in to comment.