From d3f20eda6224ba8f821116640d9896ded165e786 Mon Sep 17 00:00:00 2001 From: cryptosharks131 <38626122+cryptosharks131@users.noreply.github.com> Date: Sun, 13 Feb 2022 16:23:28 -0500 Subject: [PATCH] v1.0.3 (#40) --- README.md | 5 + gui/forms.py | 2 + gui/migrations/0020_auto_20220126_2113.py | 71 ++++++++++++ gui/models.py | 14 ++- gui/serializers.py | 3 + gui/templates/advanced.html | 37 +++++- gui/templates/balances.html | 10 +- gui/templates/base.html | 2 +- gui/templates/channels.html | 6 +- gui/templates/closures.html | 41 +++++++ gui/templates/fee_rates.html | 66 +++++++++++ gui/templates/home.html | 24 ++-- gui/templates/open_list.html | 50 ++++---- gui/urls.py | 6 +- gui/views.py | 135 ++++++++++++++++++++-- jobs.py | 66 +++++++++-- rebalancer.py | 13 +-- 17 files changed, 471 insertions(+), 80 deletions(-) create mode 100644 gui/migrations/0020_auto_20220126_2113.py create mode 100644 gui/templates/closures.html create mode 100644 gui/templates/fee_rates.html diff --git a/README.md b/README.md index b726d339..73b55e65 100644 --- a/README.md +++ b/README.md @@ -128,6 +128,11 @@ 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 Fee Rates +LNDg will make suggestions on an adjustment to the current set outbound fee rate for each channel. This uses historical payment and forwarding data over the last 7 days to drive suggestions. + +You may see another adjustment right after setting the new suggested fee rate on some channels. This is normal and you should wait ~24 hours before changing the fee rate again on any given channel. + ### 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: diff --git a/gui/forms.py b/gui/forms.py index 1948ab92..e5d4d450 100644 --- a/gui/forms.py +++ b/gui/forms.py @@ -79,6 +79,8 @@ class AutoRebalanceForm(forms.Form): (3, 'ar_in_target'), (4, 'ar_out_target'), (5, 'ar_enabled'), + (6, 'ar_max_cost'), + (7, 'channel_state'), ] class UpdateChannel(forms.Form): diff --git a/gui/migrations/0020_auto_20220126_2113.py b/gui/migrations/0020_auto_20220126_2113.py new file mode 100644 index 00000000..858580b5 --- /dev/null +++ b/gui/migrations/0020_auto_20220126_2113.py @@ -0,0 +1,71 @@ +# Generated by Django 3.2.7 on 2022-01-26 21:13 + +from django.db import migrations, models +import django.utils.timezone + +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-MaxCost%').exists(): + ar_max_cost = float(settings.objects.filter(key='AR-MaxCost%')[0].value) + else: + ar_max_cost = 0.75 + channels.objects.all().update(ar_max_cost=ar_max_cost*100) + except Exception as e: + print('Migration step failed:', str(e)) + +def revert_defaults(apps, schedma_editor): + pass + +class Migration(migrations.Migration): + + dependencies = [ + ('gui', '0019_auto_20220122_1009'), + ] + + operations = [ + migrations.AddField( + model_name='channels', + name='ar_max_cost', + field=models.IntegerField(default=65), + ), + migrations.AddField( + model_name='channels', + name='last_update', + field=models.DateTimeField(default=django.utils.timezone.now), + ), + migrations.AddField( + model_name='channels', + name='local_disabled', + field=models.BooleanField(default=False), + preserve_default=False, + ), + migrations.AddField( + model_name='channels', + name='remote_disabled', + field=models.BooleanField(default=False), + preserve_default=False, + ), + migrations.AddField( + model_name='payments', + name='cleaned', + field=models.BooleanField(default=False), + ), + migrations.AlterField( + model_name='invoices', + name='message', + field=models.CharField(max_length=500, null=True), + ), + migrations.RunPython(update_defaults, revert_defaults), + migrations.AlterField( + model_name='channels', + name='ar_max_cost', + field=models.IntegerField(), + ), + migrations.AlterField( + model_name='channels', + name='last_update', + field=models.DateTimeField(), + ), + ] diff --git a/gui/models.py b/gui/models.py index db489a57..0a5ce8c6 100644 --- a/gui/models.py +++ b/gui/models.py @@ -13,6 +13,7 @@ class Payments(models.Model): chan_out_alias = models.CharField(null=True, max_length=32) keysend_preimage = models.CharField(null=True, max_length=64) message = models.CharField(null=True, max_length=255) + cleaned = models.BooleanField(default=False) class Meta: app_label = 'gui' @@ -41,7 +42,7 @@ class Invoices(models.Model): chan_in = models.IntegerField(null=True) chan_in_alias = models.CharField(null=True, max_length=32) keysend_preimage = models.CharField(null=True, max_length=64) - message = models.CharField(null=True, max_length=255) + message = models.CharField(null=True, max_length=500) index = models.IntegerField() class Meta: app_label = 'gui' @@ -74,14 +75,18 @@ class Channels(models.Model): alias = models.CharField(max_length=32) local_base_fee = models.IntegerField() local_fee_rate = models.IntegerField() + local_disabled = models.BooleanField() remote_base_fee = models.IntegerField() remote_fee_rate = models.IntegerField() + remote_disabled = models.BooleanField() is_active = models.BooleanField() is_open = models.BooleanField() + last_update = models.DateTimeField() auto_rebalance = models.BooleanField(default=False) ar_amt_target = models.BigIntegerField() ar_in_target = models.IntegerField(default=100) ar_out_target = models.IntegerField() + ar_max_cost = models.IntegerField() def save(self, *args, **kwargs): if not self.ar_out_target: @@ -98,6 +103,13 @@ def save(self, *args, **kwargs): LocalSettings(key='AR-Target%', value='0.05').save() amt_setting = 0.05 self.ar_amt_target = int(amt_setting * self.capacity) + if not self.ar_max_cost: + if LocalSettings.objects.filter(key='AR-MaxCost%').exists(): + cost_setting = float(LocalSettings.objects.filter(key='AR-MaxCost%')[0].value) + else: + LocalSettings(key='AR-MaxCost%', value='0.65').save() + cost_setting = 0.65 + self.ar_max_cost = int(cost_setting * 100) super(Channels, self).save(*args, **kwargs) class Meta: diff --git a/gui/serializers.py b/gui/serializers.py index d5d8d7b5..b5ea05e3 100644 --- a/gui/serializers.py +++ b/gui/serializers.py @@ -41,6 +41,9 @@ class ChannelSerializer(serializers.HyperlinkedModelSerializer): is_active = serializers.ReadOnlyField() is_open = serializers.ReadOnlyField() num_updates = serializers.ReadOnlyField() + local_disabled = serializers.ReadOnlyField() + remote_disabled = serializers.ReadOnlyField() + last_update = serializers.ReadOnlyField() class Meta: model = Channels exclude = [] diff --git a/gui/templates/advanced.html b/gui/templates/advanced.html index 6a85bdc0..c3ffa55a 100644 --- a/gui/templates/advanced.html +++ b/gui/templates/advanced.html @@ -14,11 +14,13 @@
Channel ID | +Capacity | +Closing TXID | +Settled Balance | +Locked Balance | +Close Height | +Close Type | +Opener | +Closer | +
---|---|---|---|---|---|---|---|---|
{{ closure.chan_id }} | +{{ closure.capacity|intcomma }} | +{{ closure.closing_tx_hash }} | +{{ closure.settled_balance|intcomma }} | +{{ closure.time_locked_balance|intcomma }} | +{{ closure.close_height|intcomma }} | +{% if closure.close_type == 0 %}Cooperative{% elif closure.close_type == 1 %}Local Force{% elif closure.close_type == 2 %}Remote Force{% elif closure.close_type == 3 %}Breach{% elif closure.close_type == 4 %}Funding Cancelled{% elif closure.close_type == 5 %}Abandoned{% else %}{{ closure.close_type }}{% endif %} | +{% if closure.open_initiator == 0 %}Unknown{% elif closure.open_initiator == 1 %}Local{% elif closure.open_initiator == 2 %}Remote{% elif closure.open_initiator == 3 %}Both{% else %}{{ closure.open_initiator }}{% endif %} | +{% if closure.close_initiator == 0 %}Unknown{% elif closure.close_initiator == 1 %}Local{% elif closure.close_initiator == 2 %}Remote{% elif closure.close_initiator == 3 %}Both{% else %}{{ closure.close_initiator }}{% endif %} | +
Channel ID | +Peer Alias | +Capacity | +Outbound Liquidity | ++ | Inbound Liquidity | +7Day Flow | +Out Rate | +Rebal Rate | +Assisted Ratio | +Adjustment | +Suggested Rate | +oRate | +oBase | +Max Cost | +iRate | +iBase | +
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
{{ channel.chan_id }} | +{{ channel.alias }} | +{{ channel.capacity|intcomma }} | +{{ channel.local_balance|intcomma }} ({{ channel.out_percent }}%) | +{% if channel.in_percent == 0 %}{% elif channel.out_percent == 0 %}{% else %}{% endif %} |
+ {{ channel.remote_balance|intcomma }} ({{ channel.in_percent }}%) | +{% if channel.net_routed_7day > 0 %}OUT{% elif channel.net_routed_7day < 0 %}IN{% else %}---{% endif %}{% if channel.net_routed_7day != 0 %} | {{ channel.net_routed_7day }}{% endif %} | +{{ channel.out_rate }} | +{{ channel.rebal_ppm }} | +{{ channel.assisted_ratio }} | +{{ channel.adjustment }} | +{{ channel.new_rate|intcomma }} | ++ + | +{{ channel.local_base_fee|intcomma }} | +{{ channel.ar_max_cost }}% | +{{ channel.remote_fee_rate|intcomma }} | +{{ channel.remote_base_fee|intcomma }} | +