mirror of
https://github.com/aljazceru/plugins.git
synced 2025-12-19 06:04:20 +01:00
122 lines
3.4 KiB
Python
Executable File
122 lines
3.4 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
from pyln.client import Plugin, RpcError
|
|
from threading import Timer
|
|
import os
|
|
import json
|
|
import traceback
|
|
|
|
|
|
plugin = Plugin()
|
|
|
|
|
|
def load_state(path):
|
|
try:
|
|
state = json.loads(open(path, 'r').read())
|
|
except Exception:
|
|
print("Could not read state file, creating a new one.")
|
|
return {'channels': {}}
|
|
return state
|
|
|
|
|
|
def save_state(path, state):
|
|
"""Atomically save the new state to the state_file.
|
|
"""
|
|
tmppath = path + '.tmp'
|
|
with open(tmppath, 'w') as f:
|
|
f.write(json.dumps(state, indent=2))
|
|
os.rename(tmppath, path)
|
|
|
|
|
|
def is_connectable(rpc, node_id):
|
|
nodes = rpc.listnodes(node_id)
|
|
|
|
if len(nodes) == 0:
|
|
return False
|
|
|
|
print(nodes)
|
|
|
|
|
|
def maybe_open_channel(desired, rpc):
|
|
peers = rpc.listpeers(desired['node_id'])['peers']
|
|
|
|
if 'satoshi' in desired:
|
|
desired['amount'] = "{}sat".format(desired['satoshi'])
|
|
del desired['satoshi']
|
|
|
|
if peers == []:
|
|
# Need to connect first, and then open a channel
|
|
# if not is_connectable(rpc, desired['node_id']):
|
|
# print("No address known for {}, cannot connect.".format(desired['node_id']))
|
|
|
|
try:
|
|
rpc.connect(desired['node_id'])
|
|
except RpcError as re:
|
|
print("Could not connect to peer: ".format(re.error))
|
|
return
|
|
peer = rpc.listpeers(desired['node_id'])['peers'][0]
|
|
else:
|
|
peer = peers[0]
|
|
|
|
channel_states = [c['state'] for c in peer['channels']]
|
|
|
|
if peer is None or len(peer['channels']) == 0:
|
|
# Just open it, we don't have one yet
|
|
# TODO(cdecker) Check balance before actually opening
|
|
rpc.fundchannel(**desired)
|
|
|
|
elif 'CHANNELD_NORMAL' in channel_states:
|
|
# Already in the desired state, nothing to do.
|
|
return
|
|
elif channel_states == ['ONCHAIND']:
|
|
# If our only channel is in state ONCHAIND it's probably time
|
|
# to open a new one
|
|
rpc.connect(desired['node_id'])
|
|
rpc.fundchannel(**desired)
|
|
|
|
|
|
def check_channels(plugin):
|
|
"""Load actual and desired states, and try to reconcile.
|
|
"""
|
|
state = load_state(plugin.state_file)
|
|
print(state)
|
|
for c in state['channels'].values():
|
|
try:
|
|
maybe_open_channel(c, plugin.rpc)
|
|
except Exception:
|
|
plugin.log(f'Error attempting to open a channel with {c["node_id"]}.')
|
|
traceback.print_exc()
|
|
Timer(30, check_channels, args=[plugin]).start()
|
|
|
|
|
|
@plugin.method('addpersistentchannel')
|
|
def add_persistent_channel(node_id, satoshi, plugin, feerate='normal',
|
|
announce=True):
|
|
"""Add a persistent channel to the state map.
|
|
|
|
The persistent-channels plugin will ensure that the channel is
|
|
opened as soon as possible and re-opened should it get closed. The
|
|
parameters are identical to `fundchannel`.
|
|
|
|
"""
|
|
state = load_state(plugin.state_file)
|
|
state['channels'][node_id] = {
|
|
'node_id': node_id,
|
|
'satoshi': satoshi,
|
|
'feerate': feerate,
|
|
'announce': announce,
|
|
}
|
|
save_state(plugin.state_file, state)
|
|
maybe_open_channel(state['channels'][node_id], plugin.rpc)
|
|
|
|
|
|
@plugin.init()
|
|
def init(options, configuration, plugin):
|
|
# This is the file in which we'll store all of our state (mostly
|
|
# desired channels for now)
|
|
plugin.state_file = os.path.join(configuration['lightning-dir'],
|
|
"persistent-channels.json")
|
|
check_channels(plugin)
|
|
|
|
|
|
plugin.run()
|