Add c-lightning support

This commit is contained in:
yzernik 2022-05-07 15:37:20 -07:00
parent 80155f1fb7
commit a3408e687c
No known key found for this signature in database
GPG key ID: D761F27D9B20BA52
11 changed files with 317 additions and 6 deletions

22
clightning-config.ini Normal file
View file

@ -0,0 +1,22 @@
[node]
network=testnet
[clightning]
enabled=true
[bitcoin]
rpc_host=localhost
rpc_port=18332
rpc_user=devuser
rpc_pass=devpass
zeromq_hashblock_port=28334
[tor]
proxy_ip=localhost
proxy_port=9150
[webadmin]
enabled=true
username=devuser
password=devpass

View file

@ -0,0 +1,25 @@
[node]
network=testnet
[lnd]
external_host=clightning_server
port=19735
[clightning]
enabled=true
[bitcoin]
rpc_host=bitcoin-core
rpc_port=18332
rpc_user=devuser
rpc_pass=devpass
zeromq_hashblock_port=28334
[tor]
proxy_ip=tor-node
proxy_port=9050
[webadmin]
enabled=true
username=devuser
password=devpass

View file

@ -19,6 +19,16 @@ services:
environment:
- NETWORK=mainnet
clightning_client:
command:
- --bitcoin-rpcconnect=bitcoin-core
- --bitcoin-rpcuser=devuser
- --bitcoin-rpcpassword=devpass
- --network=mainnet
- --plugin-dir=/usr/libexec/c-lightning/plugins
- --alias=myitestnode
- --log-level=debug
# squeaknode:
# environment:
# - NETWORK=mainnet

View file

@ -15,6 +15,32 @@ services:
-zmqpubhashblock=tcp://0.0.0.0:28334
-zmqpubhashtx=tcp://0.0.0.0:28334
clightning_client:
command:
- --bitcoin-rpcconnect=bitcoin-core
- --bitcoin-rpcuser=devuser
- --bitcoin-rpcpassword=devpass
- --network=testnet
- --plugin-dir=/usr/libexec/c-lightning/plugins
- --alias=myclientningclient
- --log-level=debug
- --rpc-file=/root/.lightning/lightning-rpc
- --rpc-file-mode=0777
- --proxy=tor-node:9050
clightning_server:
command:
- --bitcoin-rpcconnect=bitcoin-core
- --bitcoin-rpcuser=devuser
- --bitcoin-rpcpassword=devpass
- --network=testnet
- --plugin-dir=/usr/libexec/c-lightning/plugins
- --alias=myclientningserver
- --log-level=debug
- --rpc-file=/root/.lightning/lightning-rpc
- --rpc-file-mode=0777
- --proxy=tor-node:9050
# squeaknode:
# environment:
# - NETWORK=testnet

View file

@ -57,6 +57,45 @@ services:
- net.ipv6.conf.all.disable_ipv6=0
entrypoint: ["./start-lnd.sh"]
clightning_client:
image: elementsproject/lightningd:v0.11.0.1
container_name: clightning_client
expose:
- "9735"
- "19735"
- "9835"
- "10009"
ports:
- 10010:10009
- 9835:9835
volumes:
- ~/.lightning:/root/.lightning
- tor-dir:/var/lib/tor
links:
- "bitcoin-core:bitcoin-core"
depends_on:
- "tor-node"
sysctls:
- net.ipv6.conf.all.disable_ipv6=0
clightning_server:
image: elementsproject/lightningd:v0.11.0.1
container_name: clightning_server
expose:
- "9735"
- "19735"
- "9835"
- "10009"
volumes:
- clightning_server_dir:/root/.lightning
- tor-dir:/var/lib/tor
links:
- "bitcoin-core:bitcoin-core"
depends_on:
- "tor-node"
sysctls:
- net.ipv6.conf.all.disable_ipv6=0
tor-node:
image: osminogin/tor-simple
container_name: tor-node
@ -89,10 +128,36 @@ services:
- net.ipv6.conf.all.disable_ipv6=0
entrypoint: ["./entrypoint.sh"]
squeaknode_clightning:
image: squeaknode
container_name: squeaknode_clightning
build:
context: ../
dockerfile: Dockerfile
depends_on:
- "lnd_server"
volumes:
- clightning_server_dir:/root/.lightning
- squeaknode_clightning_dir:/root/.sqk
- ./config-clightning.ini:/config.ini
ports:
- 18558:18555
- 12996:12994
links:
- "bitcoin-core:bitcoin-core"
- "lnd_server:lnd"
sysctls:
- net.ipv6.conf.all.disable_ipv6=0
entrypoint: ["./entrypoint.sh"]
volumes:
lnd_server_dir:
driver: local
clightning_server_dir:
driver: local
squeaknode_dir:
driver: local
squeaknode_clightning_dir:
driver: local
tor-dir:
driver: local

View file

@ -1,4 +1,5 @@
alembic==1.7.1
docutils==0.17.1
Flask==2.0.1
flask-cors==3.0.10
flask-login==0.5.0
@ -7,6 +8,7 @@ googleapis-common-protos==1.53.0
importlib_resources==1.4.0
mypy-protobuf==2.9
protobuf==3.17.3
pyln-client==0.10.1
PySocks==1.7.1
python-bitcoinlib==0.11.0
pyzmq==22.3.0

View file

@ -56,6 +56,9 @@ BITCOIN_RPC_PORT = {
}
DEFAULT_LND_PORT = 9735
DEFAULT_LND_RPC_PORT = 10009
DEFAULT_CLIGHTNING_DIR = ".lightning"
DEFAULT_CLIGHTNING_RPC_FILE = str(
Path.home() / DEFAULT_CLIGHTNING_DIR / 'lightning-rpc')
DEFAULT_SQK_DIR = ".sqk"
DEFAULT_SQK_DIR_PATH = str(Path.home() / DEFAULT_SQK_DIR)
DEFAULT_LND_HOST = "localhost"
@ -92,6 +95,13 @@ class LndConfig(Config):
macaroon_path = key(cast=str, required=False, default="")
@section('clightning')
class CLightningConfig(Config):
enabled = key(cast=bool, required=False, default=False)
rpc_file = key(cast=str, required=False,
default=DEFAULT_CLIGHTNING_RPC_FILE)
@section('tor')
class TorConfig(Config):
proxy_ip = key(cast=str, required=False, default="")
@ -173,6 +183,7 @@ class TwitterConfig(Config):
class SqueaknodeConfig(Config):
bitcoin = group_key(BitcoinConfig)
lnd = group_key(LndConfig)
clightning = group_key(CLightningConfig)
tor = group_key(TorConfig)
server = group_key(ServerConfig)
rpc = group_key(RpcConfig)

View file

@ -0,0 +1,133 @@
# MIT License
#
# Copyright (c) 2020 Jonathan Zernik
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import logging
import time
import uuid
from pyln.client import LightningRpc
from squeaknode.core.exception import InvoiceSubscriptionError
from squeaknode.lightning.info import Info
from squeaknode.lightning.invoice import Invoice
from squeaknode.lightning.invoice_stream import InvoiceStream
from squeaknode.lightning.lightning_client import LightningClient
from squeaknode.lightning.pay_req import PayReq
from squeaknode.lightning.payment import Payment
logger = logging.getLogger(__name__)
class CLightningClient(LightningClient):
"""Access a c-lightning instance using the Python API."""
def __init__(
self,
rpc_path: str,
) -> None:
self.rpc_path = rpc_path
def init(self):
# Create an instance of the LightningRpc object using the Core Lightning daemon on your computer.
logger.info('initializing clightning client: {}'.format(self.rpc_path))
self.lrpc = LightningRpc(self.rpc_path)
def pay_invoice(self, payment_request: str) -> Payment:
payment = self.lrpc.pay(payment_request)
if payment['status'] == 'complete':
return Payment(
payment_preimage=bytes.fromhex(payment['payment_preimage']),
payment_error='',
)
else:
return Payment(
payment_preimage=b'',
payment_error='Payment failed.',
)
def get_info(self) -> Info:
info = self.lrpc.getinfo()
pubkey = info['id']
binding = info.get('binding')
uris = []
if binding:
for b in binding:
address = b['address']
if ':' not in address: # TODO: Change type of uri to LightningAddress.
port = b['port']
uri = f"{pubkey}@{address}:{port}"
uris.append(uri)
return Info(
uris=uris,
)
def decode_pay_req(self, payment_request: str) -> PayReq:
pay_req = self.lrpc.decodepay(payment_request)
return PayReq(
payment_hash=bytes.fromhex(pay_req['payment_hash']),
payment_point=b'', # TODO: Use real payment point.
num_msat=pay_req['amount_msat'].millisatoshis,
destination=pay_req['payee'],
timestamp=int(pay_req['created_at']),
expiry=int(pay_req['expiry']),
)
def create_invoice(self, preimage: bytes, amount_msat: int) -> Invoice:
created_invoice = self.lrpc.invoice(
amount_msat,
label=str(uuid.uuid4()),
description="Squeaknode invoice",
preimage=preimage.hex(),
)
creation_time = int(time.time())
expiry = int(created_invoice['expires_at']) - creation_time
return Invoice(
r_hash=bytes.fromhex(created_invoice['payment_hash']),
payment_request=created_invoice['bolt11'],
value_msat=amount_msat,
settled=False,
settle_index=0,
creation_date=creation_time,
expiry=expiry,
)
def subscribe_invoices(self, settle_index: int) -> InvoiceStream:
# subscribe_invoices_request = lnd_pb2.InvoiceSubscription(
# settle_index=settle_index,
# )
# subscribe_result = self.stub.SubscribeInvoices(
# subscribe_invoices_request,
# )
# return InvoiceStream(
# cancel=subscribe_result.cancel,
# result_stream=iter(subscribe_result),
# )
def cancel_fn():
return None
def get_invoice_stream():
raise InvoiceSubscriptionError()
return InvoiceStream(
cancel=cancel_fn,
result_stream=get_invoice_stream(),
)

View file

@ -34,6 +34,13 @@ logger = logging.getLogger(__name__)
class LightningClient(ABC):
@abstractmethod
def init(self):
"""Initialize the lightning client.
Returns:
None
"""
@abstractmethod
def get_info(self) -> Info:
"""Get info about the lightning node.

View file

@ -34,6 +34,7 @@ from squeaknode.core.squeak_core import SqueakCore
from squeaknode.db.db_engine import get_connection_string
from squeaknode.db.db_engine import get_engine
from squeaknode.db.squeak_db import SqueakDb
from squeaknode.lightning.clightning_lightning_client import CLightningClient
from squeaknode.lightning.lnd_lightning_client import LNDLightningClient
from squeaknode.node.node_settings import NodeSettings
from squeaknode.node.payment_processor import PaymentProcessor
@ -48,6 +49,7 @@ from squeaknode.server.app import SqueakPeerWebServer
from squeaknode.server.squeak_peer_server_handler import SqueakPeerServerHandler
from squeaknode.twitter.twitter_forwarder import TwitterForwarder
logger = logging.getLogger(__name__)
@ -118,12 +120,17 @@ class SqueakNode:
self.node_settings = NodeSettings(self.squeak_db)
def create_lightning_client(self):
self.lightning_client = LNDLightningClient(
self.config.lnd.host,
self.config.lnd.rpc_port,
self.config.lnd.tls_cert_path,
self.config.lnd.macaroon_path,
)
if self.config.clightning.enabled:
self.lightning_client = CLightningClient(
self.config.clightning.rpc_file,
)
else:
self.lightning_client = LNDLightningClient(
self.config.lnd.host,
self.config.lnd.rpc_port,
self.config.lnd.tls_cert_path,
self.config.lnd.macaroon_path,
)
def create_bitcoin_client(self):
self.bitcoin_client = BitcoinCoreClient(

View file

@ -189,6 +189,9 @@ class MockLightningClient(LightningClient):
self.payment = payment
self.invoice_stream = invoice_stream
def init(self):
pass
def get_info(self):
return self.info