mirror of
https://github.com/aljazceru/plugins.git
synced 2025-12-19 14:14:20 +01:00
persistent-channels: First implementation of persistent channels.
Signed-off-by: Christian Decker <decker.christian@gmail.com>
This commit is contained in:
11
README.md
11
README.md
@@ -1,2 +1,9 @@
|
||||
# plugins
|
||||
Community curated plugins for c-lightning
|
||||
# Plugins for c-lightning
|
||||
|
||||
Community curated plugins for c-lightning.
|
||||
|
||||
## Available plugins
|
||||
|
||||
| Name | Short description |
|
||||
|---------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| persistent-channels | The persistent channels plugin allows you to describe a number of channels you'd like to have open at any time and the plugin will attempt to maintain that state. |
|
||||
|
||||
13
persistent-channels/README.md
Normal file
13
persistent-channels/README.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# Persistent Channels plugin
|
||||
|
||||
`lightningd` automatically tracks channels internally and will make
|
||||
sure to reconnect to a peer if it has a channel open with it. However,
|
||||
it only tracks the channel itself, it does not re-open a channel that
|
||||
was closed.
|
||||
|
||||
The persistent channels plugin allows you to describe a number of
|
||||
channels you'd like to have open at any time and the plugin will
|
||||
attempt to maintain that state. The plugin keeps a list of desired
|
||||
channels that should be opened and operational and will check every 30
|
||||
seconds if it needs to open a new channel, or re-open a channel that
|
||||
is currently being closed.
|
||||
101
persistent-channels/persistent-channels.py
Executable file
101
persistent-channels/persistent-channels.py
Executable 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()
|
||||
2
persistent-channels/requirements.txt
Normal file
2
persistent-channels/requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
pylightning==0.0.6
|
||||
|
||||
Reference in New Issue
Block a user