Implemented c-lightning support with short channel IDs

This commit is contained in:
Djuri Baars 2021-12-18 18:30:02 +01:00
parent 66fd5da20d
commit 93af2a2731
7 changed files with 117 additions and 22 deletions

View File

@ -7,8 +7,8 @@ from yachalk import chalk
class CheckRing:
def __init__(self, lnd, output, pubkeys_file, write_channels, show_fees, channels_file):
self.lnd = lnd
def __init__(self, client, output, pubkeys_file, write_channels, show_fees, channels_file):
self.client = client
self.output = output
self.pubkeys_file = pubkeys_file
self.show_fees = show_fees
@ -34,10 +34,11 @@ class CheckRing:
# pubkeys format is <pubkey>,<telegram username> to be able to mimic the manual pubkey overview with usernames
pubkey = pubkeyInfo.split(',')
try:
response = self.lnd.get_node_channels(pubkey[0])
alias = self.client.get_node_alias(pubkey[0])
response = self.client.get_node_channels(pubkey[0])
print("%s" %
(chalk.yellow(response.node.alias)))
(chalk.yellow(alias)))
channelTo = pubkeys[(idx+1) % (len(pubkeys))].split(',')[0]
hasChannel = False
@ -62,9 +63,9 @@ class CheckRing:
print(chalk.green("Channel is open with ID: %s") % channelId)
if self.show_fees:
response = self.lnd.get_edge(int(channelId))
node1 = self.lnd.get_node(response.node1_pub)
node2 = self.lnd.get_node(response.node2_pub)
response = self.client.get_edge(int(channelId))
node1 = self.client.get_node(response.node1_pub)
node2 = self.client.get_node(response.node2_pub)
disabled = response.node1_policy.disabled or response.node2_policy.disabled
self.print_channel(
channelInfo, node1.alias, node2.alias, disabled)

52
clightning.py Normal file
View File

@ -0,0 +1,52 @@
from os.path import expanduser
from pyln.client import LightningRpc
import argparse
from lnclient import LNClient
class CLightning(LNClient):
def __init__(self, clrpc):
self.rpc = LightningRpc(clrpc)
# TODO: handle invalid channel ids
def get_edge(self, channel_id):
response = self.rpc.listchannels(short_channel_id=channel_id)['channels']
return self.convert_cl_to_lnd(response)
def get_node_channels(self, pub_key):
response =self.rpc.listchannels(source=pub_key)['channels']
output = argparse.Namespace()
output.channels = []
for r in response:
output.channels.append(self.get_edge(r['short_channel_id']))
return output
def get_node_alias(self, pub_key):
return self.get_node(pub_key).alias
def get_node(self, pub_key):
return argparse.Namespace(**self.rpc.listnodes(pub_key)['nodes'][0])
def convert_cl_to_lnd(self, d):
output = argparse.Namespace()
output.channel_id = d[0]['short_channel_id']
output.node1_pub = d[0]['source']
output.node2_pub = d[0]['destination']
output.node1_policy = argparse.Namespace()
output.node2_policy = argparse.Namespace()
output.node1_policy.disabled = not d[0]['active']
if (len(d) > 1):
output.node2_policy.disabled = not d[1]['active']
output.node1_policy.fee_base_msat = d[0]['base_fee_millisatoshi']
output.node1_policy.fee_rate_milli_msat = d[0]['fee_per_millionth']
if (len(d) > 1):
output.node2_policy.fee_base_msat = d[1]['base_fee_millisatoshi']
output.node2_policy.fee_rate_milli_msat = d[1]['fee_per_millionth']
return output

18
lnclient.py Normal file
View File

@ -0,0 +1,18 @@
from abc import ABC, abstractmethod
class LNClient(ABC):
@abstractmethod
def get_edge(self, channel_id):
pass
@abstractmethod
def get_node_channels(self, pub_key):
pass
@abstractmethod
def get_node_alias(self, pub_key):
pass
@abstractmethod
def get_node(self, pub_key):
pass

3
lnd.py
View File

@ -8,11 +8,12 @@ from grpc_gen import router_pb2 as lnrouter
from grpc_gen import router_pb2_grpc as lnrouterrpc
from grpc_gen import lightning_pb2 as ln
from grpc_gen import lightning_bp2_grpc as lnrpc
from lnclient import LNClient
MESSAGE_SIZE_MB = 50 * 1024 * 1024
class Lnd:
class Lnd(LNClient):
def __init__(self, lnd_dir, server):
os.environ["GRPC_SSL_CIPHER_SUITES"] = "HIGH+ECDSA"
lnd_dir = expanduser(lnd_dir)

View File

@ -2,3 +2,4 @@ googleapis-common-protos==1.53.0
grpcio==1.39.0
protobuf==3.17.3
yachalk==0.1.4
pyln-client==0.10.2.post0

View File

@ -1,5 +1,6 @@
import argparse
import sys
from clightning import CLightning
from lnd import Lnd
from output import Output
@ -9,19 +10,22 @@ from checkring import CheckRing
class RingTools:
def __init__(self, arguments):
self.lnd = Lnd(arguments.lnddir, arguments.grpc)
self.output = Output(self.lnd)
if (arguments.client == 'cl'):
self.client = CLightning(arguments.clrpc)
else:
self.client = Lnd(arguments.lnddir, arguments.grpc)
self.output = Output(self.client)
self.arguments = arguments
def start(self):
if self.arguments.function == "status":
Status(self.lnd,
Status(self.client,
self.output,
self.arguments.channels_file,
self.arguments.loop,
self.arguments.show_fees).run()
elif self.arguments.function == "check":
CheckRing(self.lnd,
CheckRing(self.client,
self.output,
self.arguments.pubkeys_file,
self.arguments.write_channels,
@ -47,6 +51,15 @@ def get_argument_parser():
"like to use",
default="help",
)
parser.add_argument(
"--client",
choices=['lnd', 'cl'],
default="lnd",
dest="client",
help="(default: lnd) Which client to use",
)
#If nodeos is Umbrel use the default umbrel lnd location
lnd_dir = "~/.lnd"
if is_umbrel():
@ -65,6 +78,13 @@ def get_argument_parser():
help="(default localhost:10009) lnd gRPC endpoint",
)
parser.add_argument(
"--clrpc",
default="~/.lightning/bitcoin/lightning-rpc",
dest="clrpc",
help="(default ~/.lightning/bitcoin/lightning-rpc) C-Lightning unix-socket",
)
status_group = parser.add_argument_group(
"status",
"Get the current status of all channels",

View File

@ -8,8 +8,8 @@ LOOP_SLEEP_TIME = 10
class Status:
def __init__(self, lnd, output, channels_file, keep_loop, show_fees):
self.lnd = lnd
def __init__(self, client, output, channels_file, keep_loop, show_fees):
self.client = client
self.output = output
self.channels_file = channels_file
self.keep_loop = keep_loop
@ -37,15 +37,17 @@ class Status:
def once(self):
channels = self.read_file(self.channels_file)
for channelID in channels:
if len(channelID) != 18:
continue
if not channelID.isnumeric():
continue
# TODO: Fix this with short channel ids or convert between formats
# if len(channelID) != 18:
# continue
# if not channelID.isnumeric():
# continue
try:
response = self.lnd.get_edge(int(channelID))
node1 = self.lnd.get_node(response.node1_pub)
node2 = self.lnd.get_node(response.node2_pub)
response = self.client.get_edge(channelID)
node1 = self.client.get_node(response.node1_pub)
node2 = self.client.get_node(response.node2_pub)
disabled = response.node1_policy.disabled or response.node2_policy.disabled
self.print_channel(response, node1.alias, node2.alias, disabled)
except grpc.RpcError as e: