mirror of
https://github.com/aljazceru/plugins.git
synced 2025-12-21 15:14:19 +01:00
drain: adds __init__.py and cleanups flake8 nits
This commit is contained in:
committed by
Christian Decker
parent
b7abc6f188
commit
04254693ab
0
drain/__init__.py
Normal file
0
drain/__init__.py
Normal file
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
from pyln.client import Plugin, Millisatoshi, RpcError
|
||||
from utils import *
|
||||
from utils import get_ours, wait_ours
|
||||
import re
|
||||
import time
|
||||
import uuid
|
||||
@@ -19,7 +19,7 @@ HTLC_FEE_EST = Millisatoshi('3000sat')
|
||||
HTLC_FEE_PAT = re.compile("^.* HTLC fee: ([0-9]+sat).*$")
|
||||
|
||||
|
||||
def setup_routing_fees(plugin, payload, route, amount, substractfees: bool=False):
|
||||
def setup_routing_fees(plugin, payload, route, amount, substractfees: bool = False):
|
||||
delay = int(plugin.get_option('cltv-final'))
|
||||
|
||||
amount_iter = amount
|
||||
@@ -66,9 +66,9 @@ def get_channel(plugin, payload, peer_id, scid=None):
|
||||
try:
|
||||
channel = next(c for c in peer['channels'] if 'short_channel_id' in c and c['short_channel_id'] == scid)
|
||||
except StopIteration:
|
||||
raise RpcError(payload['command'], payload, {'message': 'Cannot find channel %s for peer %s' % (scid, peer_id) })
|
||||
raise RpcError(payload['command'], payload, {'message': 'Cannot find channel %s for peer %s' % (scid, peer_id)})
|
||||
if channel['state'] != "CHANNELD_NORMAL":
|
||||
raise RpcError(payload['command'], payload, {'message': 'Channel %s not in state CHANNELD_NORMAL, but: %s' % (scid, channel['state']) })
|
||||
raise RpcError(payload['command'], payload, {'message': 'Channel %s not in state CHANNELD_NORMAL, but: %s' % (scid, channel['state'])})
|
||||
if not peer['connected']:
|
||||
raise RpcError(payload['command'], payload, {'message': 'Channel %s peer is not connected.' % scid})
|
||||
return channel
|
||||
@@ -152,16 +152,16 @@ def test_or_set_chunks(plugin, payload):
|
||||
|
||||
# get all spendable/receivables for our channels
|
||||
channels = {}
|
||||
for channel in plugin.rpc.listchannels(source = payload['my_id']).get('channels'):
|
||||
for channel in plugin.rpc.listchannels(source=payload['my_id']).get('channels'):
|
||||
if channel['short_channel_id'] == scid:
|
||||
continue
|
||||
try:
|
||||
spend, recv = spendable_from_scid(plugin, payload, channel['short_channel_id'], True)
|
||||
except RpcError as e:
|
||||
except RpcError:
|
||||
continue
|
||||
channels[channel['short_channel_id']] = {
|
||||
'spendable' : spend,
|
||||
'receivable' : recv,
|
||||
'spendable': spend,
|
||||
'receivable': recv,
|
||||
}
|
||||
if len(channels) == 0:
|
||||
raise RpcError(payload['command'], payload, {'message': 'Not enough usable channels to perform cyclic routing.'})
|
||||
@@ -232,13 +232,13 @@ def try_for_htlc_fee(plugin, payload, peer_id, amount, chunk, spendable_before):
|
||||
my_id = payload['my_id']
|
||||
label = payload['command'] + "-" + str(uuid.uuid4())
|
||||
payload['labels'] += [label]
|
||||
description = "%s %s %s%s [%d/%d]" % (payload['command'], payload['scid'], payload['percentage'], '%', chunk+1, payload['chunks'])
|
||||
description = "%s %s %s%s [%d/%d]" % (payload['command'], payload['scid'], payload['percentage'], '%', chunk + 1, payload['chunks'])
|
||||
invoice = plugin.rpc.invoice("any", label, description, payload['retry_for'] + 60)
|
||||
payment_hash = invoice['payment_hash']
|
||||
plugin.log("Invoice payment_hash: %s" % payment_hash)
|
||||
|
||||
# exclude selected channel to prevent unwanted shortcuts
|
||||
excludes = [payload['scid']+'/0', payload['scid']+'/1']
|
||||
excludes = [payload['scid'] + '/0', payload['scid'] + '/1']
|
||||
mychannels = plugin.rpc.listchannels(source=my_id).get('channels')
|
||||
# exclude local channels known to have too little capacity.
|
||||
# getroute currently does not do this.
|
||||
@@ -247,23 +247,23 @@ def try_for_htlc_fee(plugin, payload, peer_id, amount, chunk, spendable_before):
|
||||
continue # already added few lines above
|
||||
spend, recv = spendable_from_scid(plugin, payload, channel['short_channel_id'])
|
||||
if payload['command'] == 'drain' and recv < amount:
|
||||
excludes += [channel['short_channel_id']+'/0', channel['short_channel_id']+'/1']
|
||||
excludes += [channel['short_channel_id'] + '/0', channel['short_channel_id'] + '/1']
|
||||
if payload['command'] == 'fill' and spend < amount:
|
||||
excludes += [channel['short_channel_id']+'/0', channel['short_channel_id']+'/1']
|
||||
excludes += [channel['short_channel_id'] + '/0', channel['short_channel_id'] + '/1']
|
||||
|
||||
while int(time.time()) - start_ts < payload['retry_for']:
|
||||
if payload['command'] == 'drain':
|
||||
r = plugin.rpc.getroute(my_id, amount, riskfactor=0,
|
||||
cltv=9, fromid=peer_id, fuzzpercent=0, exclude=excludes)
|
||||
cltv=9, fromid=peer_id, fuzzpercent=0, exclude=excludes)
|
||||
route_out = {'id': peer_id, 'channel': payload['scid'], 'direction': int(my_id >= peer_id)}
|
||||
route = [route_out] + r['route']
|
||||
setup_routing_fees(plugin, payload, route, amount, True)
|
||||
if payload['command'] == 'fill':
|
||||
r = plugin.rpc.getroute(peer_id, amount, riskfactor=0,
|
||||
cltv=9, fromid=my_id, fuzzpercent=0, exclude=excludes)
|
||||
cltv=9, fromid=my_id, fuzzpercent=0, exclude=excludes)
|
||||
route_in = {'id': my_id, 'channel': payload['scid'], 'direction': int(peer_id >= my_id)}
|
||||
route = r['route'] + [route_in]
|
||||
setup_routing_fees(plugin, payload, route, amount , False)
|
||||
setup_routing_fees(plugin, payload, route, amount, False)
|
||||
|
||||
fees = route[0]['amount_msat'] - route[-1]['amount_msat']
|
||||
|
||||
@@ -276,7 +276,7 @@ def try_for_htlc_fee(plugin, payload, peer_id, amount, chunk, spendable_before):
|
||||
excludes += [worst_channel_id + '/0', worst_channel_id + '/1']
|
||||
continue
|
||||
|
||||
plugin.log("[%d/%d] Sending over %d hops to %s %s using %s fees" % (chunk+1, payload['chunks'], len(route), payload['command'], amount, fees), 'debug')
|
||||
plugin.log("[%d/%d] Sending over %d hops to %s %s using %s fees" % (chunk + 1, payload['chunks'], len(route), payload['command'], amount, fees), 'debug')
|
||||
for r in route:
|
||||
plugin.log(" - %s %14s %s" % (r['id'], r['channel'], r['amount_msat']), 'debug')
|
||||
|
||||
@@ -285,7 +285,7 @@ def try_for_htlc_fee(plugin, payload, peer_id, amount, chunk, spendable_before):
|
||||
plugin.rpc.sendpay(route, payment_hash, label)
|
||||
result = plugin.rpc.waitsendpay(payment_hash, payload['retry_for'] + start_ts - int(time.time()))
|
||||
if result.get('status') == 'complete':
|
||||
payload['success_msg'] += ["%dmsat sent over %d hops to %s %dmsat [%d/%d]" % (amount + fees, len(route), payload['command'], amount, chunk+1, payload['chunks'])]
|
||||
payload['success_msg'] += ["%dmsat sent over %d hops to %s %dmsat [%d/%d]" % (amount + fees, len(route), payload['command'], amount, chunk + 1, payload['chunks'])]
|
||||
# we need to wait for HTLC to resolve, so remaining amounts
|
||||
# can be calculated correctly for the next chunk
|
||||
wait_ours(plugin, payload['scid'], ours)
|
||||
@@ -314,7 +314,7 @@ def try_for_htlc_fee(plugin, payload, peer_id, amount, chunk, spendable_before):
|
||||
|
||||
|
||||
def read_params(command: str, scid: str, percentage: float,
|
||||
chunks: int, maxfeepercent: float, retry_for: int, exemptfee: Millisatoshi):
|
||||
chunks: int, maxfeepercent: float, retry_for: int, exemptfee: Millisatoshi):
|
||||
|
||||
# check parameters
|
||||
if command != 'drain' and command != 'fill' and command != 'setbalance':
|
||||
@@ -327,15 +327,15 @@ def read_params(command: str, scid: str, percentage: float,
|
||||
|
||||
# forge operation payload
|
||||
payload = {
|
||||
"command" : command,
|
||||
"command": command,
|
||||
"scid": scid,
|
||||
"percentage": percentage,
|
||||
"chunks": chunks,
|
||||
"maxfeepercent": maxfeepercent,
|
||||
"retry_for": retry_for,
|
||||
"exemptfee": exemptfee,
|
||||
"labels" : [],
|
||||
"success_msg" : [],
|
||||
"labels": [],
|
||||
"success_msg": [],
|
||||
}
|
||||
|
||||
# cache some often required data
|
||||
@@ -396,7 +396,7 @@ def execute(payload: dict):
|
||||
if amount < htlc_fee:
|
||||
raise RpcError(payload['command'], payload, {'message': 'channel too low to cover fees'})
|
||||
amount -= htlc_fee
|
||||
plugin.log("Trying... chunk:%s/%s spendable:%s receivable:%s htlc_fee:%s => amount:%s" % (chunk+1, payload['chunks'], spendable, receivable, htlc_fee, amount))
|
||||
plugin.log("Trying... chunk:%s/%s spendable:%s receivable:%s htlc_fee:%s => amount:%s" % (chunk + 1, payload['chunks'], spendable, receivable, htlc_fee, amount))
|
||||
|
||||
try:
|
||||
result = try_for_htlc_fee(plugin, payload, peer_id, amount, chunk, spendable)
|
||||
@@ -425,8 +425,8 @@ def execute(payload: dict):
|
||||
|
||||
|
||||
@plugin.method("drain")
|
||||
def drain(plugin, scid: str, percentage: float=100, chunks: int=0, maxfeepercent: float=0.5,
|
||||
retry_for: int=60, exemptfee: Millisatoshi=Millisatoshi(5000)):
|
||||
def drain(plugin, scid: str, percentage: float = 100, chunks: int = 0, maxfeepercent: float = 0.5,
|
||||
retry_for: int = 60, exemptfee: Millisatoshi = Millisatoshi(5000)):
|
||||
"""Draining channel liquidity with circular payments.
|
||||
|
||||
Percentage defaults to 100, resulting in an empty channel.
|
||||
@@ -438,8 +438,8 @@ def drain(plugin, scid: str, percentage: float=100, chunks: int=0, maxfeepercent
|
||||
|
||||
|
||||
@plugin.method("fill")
|
||||
def fill(plugin, scid: str, percentage: float=100, chunks: int=0, maxfeepercent: float=0.5,
|
||||
retry_for: int=60, exemptfee: Millisatoshi=Millisatoshi(5000)):
|
||||
def fill(plugin, scid: str, percentage: float = 100, chunks: int = 0, maxfeepercent: float = 0.5,
|
||||
retry_for: int = 60, exemptfee: Millisatoshi = Millisatoshi(5000)):
|
||||
"""Filling channel liquidity with circular payments.
|
||||
|
||||
Percentage defaults to 100, resulting in a full channel.
|
||||
@@ -451,8 +451,8 @@ def fill(plugin, scid: str, percentage: float=100, chunks: int=0, maxfeepercent:
|
||||
|
||||
|
||||
@plugin.method("setbalance")
|
||||
def setbalance(plugin, scid: str, percentage: float=50, chunks: int=0, maxfeepercent: float=0.5,
|
||||
retry_for: int=60, exemptfee: Millisatoshi=Millisatoshi(5000)):
|
||||
def setbalance(plugin, scid: str, percentage: float = 50, chunks: int = 0, maxfeepercent: float = 0.5,
|
||||
retry_for: int = 60, exemptfee: Millisatoshi = Millisatoshi(5000)):
|
||||
"""Brings a channels own liquidity to X percent using circular payments.
|
||||
|
||||
Percentage defaults to 50, resulting in a balanced channel.
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
from pyln.testing.fixtures import *
|
||||
from pyln.testing.fixtures import * # noqa: F401,F403
|
||||
from pyln.testing.utils import DEVELOPER
|
||||
from pyln.client import RpcError, Millisatoshi
|
||||
from utils import *
|
||||
from pyln.client import RpcError
|
||||
from .utils import get_ours, get_theirs, wait_ours, wait_for_all_htlcs
|
||||
import os
|
||||
import unittest
|
||||
import pytest
|
||||
|
||||
|
||||
pluginopt = {'plugin': os.path.join(os.path.dirname(__file__), "drain.py")}
|
||||
EXPERIMENTAL_FEATURES = int(os.environ.get("EXPERIMENTAL_FEATURES", "0"))
|
||||
|
||||
|
||||
@unittest.skipIf(not DEVELOPER, "slow gossip, needs DEVELOPER=1")
|
||||
def test_drain_and_refill(node_factory, bitcoind):
|
||||
# Scenario: first drain then refill
|
||||
@@ -37,7 +39,7 @@ def test_drain_and_refill(node_factory, bitcoind):
|
||||
# wait for each others gossip
|
||||
bitcoind.generate_block(6)
|
||||
for n in nodes:
|
||||
for scid in [scid12,scid23,scid34,scid41]:
|
||||
for scid in [scid12, scid23, scid34, scid41]:
|
||||
n.wait_channel_active(scid)
|
||||
|
||||
# do some draining and filling
|
||||
@@ -87,7 +89,7 @@ def test_fill_and_drain(node_factory, bitcoind):
|
||||
# wait for each others gossip
|
||||
bitcoind.generate_block(6)
|
||||
for n in nodes:
|
||||
for scid in [scid12,scid23,scid34,scid41]:
|
||||
for scid in [scid12, scid23, scid34, scid41]:
|
||||
n.wait_channel_active(scid)
|
||||
|
||||
# for l2 to fill scid12, it needs to send on scid23, where its funder
|
||||
@@ -128,7 +130,7 @@ def test_setbalance(node_factory, bitcoind):
|
||||
# wait for each others gossip
|
||||
bitcoind.generate_block(6)
|
||||
for n in nodes:
|
||||
for scid in [scid12,scid23,scid34,scid41]:
|
||||
for scid in [scid12, scid23, scid34, scid41]:
|
||||
n.wait_channel_active(scid)
|
||||
|
||||
# test auto 50/50 balancing
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import time
|
||||
|
||||
TIMEOUT=60
|
||||
TIMEOUT = 60
|
||||
|
||||
|
||||
def wait_for(success, timeout=TIMEOUT):
|
||||
|
||||
Reference in New Issue
Block a user