persistent-channels: First implementation of persistent channels.

Signed-off-by: Christian Decker <decker.christian@gmail.com>
This commit is contained in:
Christian Decker
2019-01-21 19:45:55 +01:00
parent cfca3a48ba
commit 39e6a6204e
4 changed files with 125 additions and 2 deletions

View File

@@ -0,0 +1,101 @@
#!/usr/bin/env python3
from lightning import Plugin
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 maybe_open_channel(desired, rpc):
peers = rpc.listpeers(desired['node_id'])['peers']
if peers == []:
# Need to connect first, and then open a channel
rpc.connect(desired['node_id'])
peer = None
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["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.method("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()