From 9882074a49392610508280e396ef85fe7bbeb926 Mon Sep 17 00:00:00 2001 From: bitromortac Date: Sun, 31 Dec 2023 09:53:26 +0100 Subject: [PATCH 1/5] node: remove code, format These are some leftovers from the rebalancing command. --- lndmanage/lib/node.py | 269 +++++++----------------------------------- 1 file changed, 44 insertions(+), 225 deletions(-) diff --git a/lndmanage/lib/node.py b/lndmanage/lib/node.py index 169cea9..6523517 100644 --- a/lndmanage/lib/node.py +++ b/lndmanage/lib/node.py @@ -1,11 +1,10 @@ import asyncio -import binascii import codecs from collections import OrderedDict, defaultdict import datetime import os import time -from typing import List, TYPE_CHECKING, Optional, Dict +from typing import List, Optional, Dict import grpc from grpc._channel import _Rendezvous @@ -20,11 +19,7 @@ import lndmanage.grpc_compiled.walletkit_pb2 as lndwalletkit import lndmanage.grpc_compiled.walletkit_pb2_grpc as lndwalletkitrpc from lndmanage.lib.network import Network -from lndmanage.lib.exceptions import PaymentTimeOut, NoRoute, OurNodeFailure -from lndmanage.lib import exceptions from lndmanage.lib.ln_utilities import ( - extract_short_channel_id_from_string, - convert_short_channel_id_to_channel_id, convert_channel_id_to_short_channel_id, local_balance_to_unbalancedness ) @@ -33,9 +28,6 @@ from lndmanage.lib.user import yes_no_question from lndmanage.lib.utilities import convert_dictionary_number_strings_to_ints from lndmanage import settings -if TYPE_CHECKING: - from lndmanage.lib.routing import Route - import logging logger = logging.getLogger(__name__) logger.addHandler(logging.NullHandler()) @@ -100,7 +92,8 @@ class LndNode: self.lnd_home, 'data/chain/bitcoin/', bitcoin_network, 'admin.macaroon') if self.lnd_host is None: - raise ValueError('if lnd_home is given, lnd_host must be given') + raise ValueError( + 'if lnd_home is given, lnd_host must be given') else: self.cert_file_path = os.path.expanduser( self.config['network']['tls_cert_file'] @@ -140,8 +133,8 @@ class LndNode: return grpc.composite_channel_credentials(cert_creds, auth_creds) async def connect_async_rpcs(self): - # This needs to be run within an async context, the loop is being used in the - # rpc connections. + # This needs to be run within an async context, the loop is being used + # in the rpc connections. logger.debug("Connecting async rpcs.") self._async_channel = grpc.aio.secure_channel( @@ -202,108 +195,6 @@ class LndNode: channel_dict = convert_dictionary_number_strings_to_ints(channel_dict) return channel_dict - @staticmethod - def lnd_hops(hops) -> List[lnd.Hop]: - return [lnd.Hop(**hop) for hop in hops] - - def _to_lnd_route(self, route: 'Route') -> lnd.Route: - """ - Converts a cleartext route to an lnd route. - """ - hops = self.lnd_hops(route.hops) - lnd_route = lnd.Route( - total_time_lock=route.total_time_lock, - total_fees=route.total_fee_msat // 1000, - total_amt=route.total_amt_msat // 1000, - hops=hops, - total_fees_msat=route.total_fee_msat, - total_amt_msat=route.total_amt_msat - ) - return lnd_route - - def get_invoice(self, amt_msat: int, memo: str) -> lnd.Invoice: - """ - Creates a new invoice with amt_msat and memo. - - :param amt_msat: int - :param memo: str - :return: Hash of invoice preimage. - """ - invoice = self._rpc.AddInvoice(lnd.Invoice( - value=amt_msat // 1000, memo=memo)) - return invoice - - def get_rebalance_invoice(self, memo) -> lnd.Invoice: - """ - Creates a zero amount invoice and gives back it's hash. - - :param memo: Comment for the invoice. - :return: Hash of the invoice preimage. - """ - invoice = self._rpc.AddInvoice(lnd.Invoice(value=0, memo=memo)) - return invoice - - def send_to_route(self, route: lnd.Route, payment_hash: bytes): - """Takes a route and sends to it.""" - - request = lndrouter.SendToRouteRequest( - route=route, - payment_hash=payment_hash, - ) - - try: - payment = self._routerrpc.SendToRouteV2( - request, timeout=GRPC_TIMEOUT_SEC, - ) - - except grpc.RpcError as e: - if e.code() == grpc.StatusCode.DEADLINE_EXCEEDED: - raise PaymentTimeOut - - raise - - if payment.HasField('failure'): - failure = payment.failure # type: lnd.Failure.FailureCode - logger.debug(f"Routing failure: {failure}") - if failure.failure_source_index == 0: - raise OurNodeFailure("Not enough funds?") - if failure.code == 12: - raise exceptions.FeeInsufficient(payment) - elif failure.code == 13: - raise exceptions.IncorrectCLTVExpiry(payment) - elif failure.code == 14: - raise exceptions.ChannelDisabled(payment) - elif failure.code == 15: - raise exceptions.TemporaryChannelFailure(payment) - elif failure.code == 18: - raise exceptions.UnknownNextPeer(payment) - elif failure.code == 19: - raise exceptions.TemporaryNodeFailure(payment) - else: - logger.info(f"Unknown error: code: {failure.code}") - raise exceptions.TemporaryChannelFailure(payment) - - return payment - - def build_route(self, amt_msat: int, outgoing_chan_id: int, - hop_pubkeys: List[str], payment_addr: bytes) -> lnd.Route: - """ Queries the routerrpc endpoint to build a route.""" - - final_cltv_delta = 144 - - # Convert hop_pubkeys to List[bytes] - hop_pubkeys = [bytes.fromhex(n) for n in hop_pubkeys] - - request = lndrouter.BuildRouteRequest( - amt_msat = amt_msat, - final_cltv_delta = final_cltv_delta, - outgoing_chan_id = outgoing_chan_id, - hop_pubkeys = hop_pubkeys, - payment_addr = payment_addr, - ) - - return self._routerrpc.BuildRoute(request, timeout=5 * 60).route - def get_raw_network_graph(self): try: graph = self._rpc.DescribeGraph(lnd.ChannelGraphRequest()) @@ -400,11 +291,15 @@ class LndNode: # interested in node2 policies = edge_info['policies'] if edge_info['node1_pub'] == self.pub_key: - policy_peer = policies[edge_info['node2_pub'] > edge_info['node1_pub']] - policy_local = policies[edge_info['node1_pub'] > edge_info['node2_pub']] + policy_peer = policies[edge_info['node2_pub'] + > edge_info['node1_pub']] + policy_local = policies[edge_info['node1_pub'] + > edge_info['node2_pub']] else: # interested in node1 - policy_peer = policies[edge_info['node1_pub'] > edge_info['node2_pub']] - policy_local = policies[edge_info['node2_pub'] > edge_info['node1_pub']] + policy_peer = policies[edge_info['node1_pub'] + > edge_info['node2_pub']] + policy_local = policies[edge_info['node2_pub'] + > edge_info['node1_pub']] except KeyError: # if channel is unknown in describegraph # we need to set the fees to some error value @@ -492,7 +387,8 @@ class LndNode: def node_id_to_channel_ids(self, open_only=False) -> Dict[str, List[int]]: node_channels_mapping = defaultdict(list) - for cid, nid in self.channel_id_to_node_id(open_only=open_only).items(): + mappings = self.channel_id_to_node_id(open_only=open_only) + for cid, nid in mappings.items(): node_channels_mapping[nid].append(cid) return node_channels_mapping @@ -513,22 +409,29 @@ class LndNode: channels = self.get_open_channels(public_only=False, active_only=False) return channels - def get_unbalanced_channels(self, unbalancedness_greater_than=0.0, excluded_channels: List[int] = None, public_only=True, active_only=True): + def get_unbalanced_channels( + self, unbalancedness_greater_than=0.0, + excluded_channels: List[int] = None, + public_only=True, active_only=True): """ Gets all channels which have an absolute unbalancedness (-1...1, -1 for outbound unbalanced, 1 for inbound unbalanced) larger than unbalancedness_greater_than. - :param unbalancedness_greater_than: unbalancedness interval, default returns all channels - :return: all channels which are more unbalanced than the specified interval + :param unbalancedness_greater_than: unbalancedness interval, + default returns all channels + :return: all channels which are more unbalanced than the + specified interval """ self.public_active_channels = \ - self.get_open_channels(public_only=public_only, active_only=active_only) + self.get_open_channels( + public_only=public_only, active_only=active_only) channels = { k: c for k, c in self.public_active_channels.items() if abs(c['unbalancedness']) >= unbalancedness_greater_than } - channels = {k: v for k, v in channels.items() if k not in (excluded_channels if excluded_channels else [])} + channels = {k: v for k, v in channels.items() if k not in ( + excluded_channels if excluded_channels else [])} return channels def get_channel_fee_policies(self): @@ -632,99 +535,6 @@ class LndNode: } return closed_channels_dict - @staticmethod - def handle_payment_error(payment_error): - """ - Handles payment errors and determines the failed channel. - - :param payment_error: - :return: int, channel_id of the failed channel. - """ - if "TemporaryChannelFailure" in payment_error: - logger.error(" Encountered temporary channel failure.") - short_channel_groups = extract_short_channel_id_from_string(payment_error) - channel_id = convert_short_channel_id_to_channel_id(*short_channel_groups) - return channel_id - - def queryroute_external(self, source_pubkey, target_pubkey, amt_msat, - ignored_nodes=(), ignored_channels={}, - use_mc=False): - """ - Queries the lnd node for a route. - - Channels and nodes can be ignored if they failed before. - - :param source_pubkey: source node public key - :type source_pubkey: str - :param target_pubkey: target node public key - :type target_pubkey: str - :param amt_msat: amount to send in msat - :type amt_msat: int - :param ignored_nodes: ignored node pubilc keys for the route - :type ignored_nodes: list[str] - :param ignored_channels: ignored channel directions for the route - :type ignored_channels: dict - :param use_mc: true if mission control should be used to blacklist - channels - :type use_mc: bool - :return: route expressed in terms of short channel ids - :rtype: list[int] - """ - amt_sat = amt_msat // 1000 - - # put safety margin when using mc based routing - # reason is that routes will not be diverse when sending with a larger - # amount later on due to fees - # the fees for the route are somewhat accounted for by the margin - if use_mc: - amt_sat = int(amt_sat * 1.02) - - # have a safety max fee in sat - max_fee = 10000 - - # convert ignored nodes to api format - if ignored_nodes: - ignored_nodes_api = [bytes.fromhex(n) for n in ignored_nodes] - else: - ignored_nodes_api = [] - - # convert ignored channels to api format - if ignored_channels: - ignored_channels_api = [] - for c, cv in ignored_channels.items(): - direction_reverse = cv['source'] > cv['target'] - ignored_channels_api.append( - lnd.EdgeLocator(channel_id=c, - direction_reverse=direction_reverse)) - else: - ignored_channels_api = [] - - logger.debug(f"Ignored for queryroutes: channels: " - f"{ignored_channels_api}, nodes: {ignored_nodes_api}") - - request = lnd.QueryRoutesRequest( - pub_key=target_pubkey, - amt=amt_sat, - final_cltv_delta=0, - fee_limit=lnd.FeeLimit(fixed=max_fee), - ignored_nodes=ignored_nodes_api, - ignored_edges=ignored_channels_api, - source_pub_key=source_pubkey, - use_mission_control=use_mc, - ) - try: - response = self._rpc.QueryRoutes(request) - except Exception as e: - if "unable to find a path" in e.details(): - raise NoRoute - else: - raise e - - # We give back only one route, as multiple routes will be deprecated - channel_route = [h.chan_id for h in response.routes[0].hops] - - return channel_route - def get_node_info(self, pub_key): """ Retrieves information on a node with a specific pub key. @@ -778,8 +588,10 @@ class LndNode: balancedness_local = 0 balancedness_remote = 0 else: - balancedness_local = self.total_local_balance / self.total_capacity - balancedness_remote = self.total_remote_balance / self.total_capacity + balancedness_local = self.total_local_balance \ + / self.total_capacity + balancedness_remote = self.total_remote_balance \ + / self.total_capacity logger.info(f"alias: {self.alias}") logger.info(f"pub key: {self.pub_key}") logger.info(f"blockheight: {self.blockheight}") @@ -788,9 +600,15 @@ class LndNode: logger.info(f"active channels: {self.total_active_channels}") logger.info(f"private channels: {self.total_private_channels}") logger.info(f"capacity: {self.total_capacity}") - logger.info(f"balancedness: l:{balancedness_local:.2%} r:{balancedness_remote:.2%}") - logger.info(f"total satoshis received (current channels): {self.total_satoshis_received}") - logger.info(f"total satoshis sent (current channels): {self.total_satoshis_sent}") + logger.info( + f"balancedness: l:{balancedness_local:.2%} " + f"r:{balancedness_remote:.2%}") + logger.info( + "total satoshis received (current channels): " + f"{self.total_satoshis_received}") + logger.info( + "total satoshis sent (current channels): " + f"{self.total_satoshis_sent}") def open_channels(self, pubkeys: List[bytes], amounts_sat: List[int], @@ -808,8 +626,8 @@ class LndNode: private=private, )) - logger.info("\n>>> WARNING: This feature is new, use at your own risk. " - "Please check the above output carefully.\n") + logger.info("\n>>> WARNING: This feature is new, use at your own " + "risk. Please check the above output carefully.\n") logger.info("\n>>> Do you want to open the channel(s) (y/n)?") if not test: if not yes_no_question('no'): @@ -834,7 +652,8 @@ class LndNode: raise ValueError(f"pubkey of unknown format {pubkey}") info = self.get_node_info(pubkey) if not info['addresses']: - raise ConnectionRefusedError(f"Could not find connection address for {pubkey}.") + raise ConnectionRefusedError( + f"Could not find connection address for {pubkey}.") logger.info(">>> Connecting to channel peer candidates.") for pubkey in pubkeys: info = self.get_node_info(pubkey) From 2fb1b378031fb09bf5f65537396fb550211f9232 Mon Sep 17 00:00:00 2001 From: bitromortac Date: Sun, 31 Dec 2023 10:07:15 +0100 Subject: [PATCH 2/5] node: use readonly macaroon by default This means that calls to `openchannels` and `update-fees` will fail. A custom lndmanage.macaroon can be created with `scripts/bakemacaroon.sh` to enable the commands. --- README.md | 12 ++++++++---- lndmanage/lib/configure.py | 13 +++++++------ lndmanage/lib/lncli.py | 2 +- lndmanage/lib/node.py | 6 +++--- lndmanage/templates/config_sample.ini | 8 +++++--- scripts/bakemacaroon.sh | 20 ++++++++++++++++++++ 6 files changed, 44 insertions(+), 17 deletions(-) create mode 100755 scripts/bakemacaroon.sh diff --git a/README.md b/README.md index 5bf8a1f..a3de1dc 100644 --- a/README.md +++ b/README.md @@ -413,11 +413,15 @@ This can be done when compiling with minimal build tags of `make && make install tags="routerrpc signrpc walletrpc"`. If you use precompiled binaries, you can ignore this. -#### Admin Macaroon and TLS cert needed +#### Macaroon and TLS cert needed If you run this tool from a different host than the lnd host, -make sure to copy `/path/to/.lnd/data/chain/bitcoin/mainnet/admin.macaroon` - and `/path/to/.lnd/tls.cert` to your local machine, which you need for later - configuration. +make sure to copy `/path/to/.lnd/data/chain/bitcoin/mainnet/readonly.macaroon` +and `/path/to/.lnd/tls.cert` to your local machine, which you need for later +configuration. + +Note that if you want to run `update-fees` or `openchannels` you will need to +create a custom macaroon, see `scripts/bakemacaroon.sh`. Don't use +`admin.macaroon`, which is not recommended for best security practices. #### Signature verification Commits and releases are signed with key `1965 063F C13B EBE2` available via diff --git a/lndmanage/lib/configure.py b/lndmanage/lib/configure.py index 81d1433..533dd76 100644 --- a/lndmanage/lib/configure.py +++ b/lndmanage/lib/configure.py @@ -36,7 +36,7 @@ def check_or_create_configuration(home_dir): :type home_dir: str """ if not os.path.exists(home_dir): # user runs for the first time - print(f"Running lndmanage for the first time.") + print("Running lndmanage for the first time.") print(f"Creating configuration folder at {home_dir}.") print("The default path can be overridden by setting the " "LNDMANAGE_HOME environment variable.") @@ -44,17 +44,18 @@ def check_or_create_configuration(home_dir): lnd_home = os.path.expanduser('~/.lnd') lnd_grpc_host = 'localhost:10009' - admin_macaroon_path = os.path.join( - lnd_home, 'data/chain/bitcoin/mainnet/admin.macaroon') + macaroon_path = os.path.join( + lnd_home, 'data/chain/bitcoin/mainnet/readonly.macaroon') tls_cert_path = os.path.join( lnd_home, 'tls.cert') if os.path.exists(lnd_home): remote = False print(f"Detected a local lnd configuration folder {lnd_home}.", ) - print("Will use admin.macaroon and tls.cert from this directory.") + print("Will use macaroon and tls.cert from this directory.") else: remote = True - print(f"IF LND RUNS ON A REMOTE HOST, CONFIGURE {home_dir}/config.ini.") + print( + f"IF LND RUNS ON A REMOTE HOST, CONFIGURE {home_dir}/config.ini.") # build config file config = configparser.ConfigParser() @@ -64,7 +65,7 @@ def check_or_create_configuration(home_dir): config.read(config_template_path) config['network']['lnd_grpc_host'] = str(lnd_grpc_host) - config['network']['admin_macaroon_file'] = str(admin_macaroon_path) + config['network']['macaroon_file'] = str(macaroon_path) config['network']['tls_cert_file'] = str(tls_cert_path) config_path = os.path.join(home_dir, 'config.ini') diff --git a/lndmanage/lib/lncli.py b/lndmanage/lib/lncli.py index b4d7048..1eb0bf8 100644 --- a/lndmanage/lib/lncli.py +++ b/lndmanage/lib/lncli.py @@ -21,7 +21,7 @@ class Lncli(object): cert_file = os.path.expanduser(config['network']['tls_cert_file']) macaroon_file = \ - os.path.expanduser(config['network']['admin_macaroon_file']) + os.path.expanduser(config['network']['macaroon_file']) lnd_host = config['network']['lnd_grpc_host'] # assemble the command for lncli for execution with flags diff --git a/lndmanage/lib/node.py b/lndmanage/lib/node.py index 6523517..f1f7fd0 100644 --- a/lndmanage/lib/node.py +++ b/lndmanage/lib/node.py @@ -90,7 +90,7 @@ class LndNode: bitcoin_network = 'regtest' if self.regtest else 'mainnet' self.macaroon_file_path = os.path.join( self.lnd_home, 'data/chain/bitcoin/', - bitcoin_network, 'admin.macaroon') + bitcoin_network, 'readonly.macaroon') if self.lnd_host is None: raise ValueError( 'if lnd_home is given, lnd_host must be given') @@ -99,7 +99,7 @@ class LndNode: self.config['network']['tls_cert_file'] ) self.macaroon_file_path = os.path.expanduser( - self.config['network']['admin_macaroon_file'] + self.config['network']['macaroon_file'] ) self.lnd_host = self.config['network']['lnd_grpc_host'] @@ -120,7 +120,7 @@ class LndNode: macaroon_bytes = f.read() macaroon = codecs.encode(macaroon_bytes, 'hex') except FileNotFoundError: - logger.error("admin.macaroon not found, please configure %s.", + logger.error("macaroon not found, please configure %s.", self.config_file) exit(1) diff --git a/lndmanage/templates/config_sample.ini b/lndmanage/templates/config_sample.ini index 5e76f1a..b68cc96 100644 --- a/lndmanage/templates/config_sample.ini +++ b/lndmanage/templates/config_sample.ini @@ -1,9 +1,11 @@ # network settings [network] lnd_grpc_host = IP:10009 -# tls and admin macaroon can be found in .lnd folder +# tls and macaroon can be found in .lnd folder tls_cert_file = /path/to/tls.cert -admin_macaroon_file = /path/to/admin.macaroon +# see `scripts/bakemacaroon.sh` to create an lndmanage macaroon to use +# openchannels and update-fees +macaroon_file = /path/to/readonly.macaroon [logging] loglevel = INFO @@ -19,4 +21,4 @@ loglevel = INFO [excluded-channels-fee-opt] # channels which are excluded from the fee optimization via the update-fees # command can be listed here by their channel ids, e.g., -# 635263839283742663=ignore \ No newline at end of file +# 635263839283742663=ignore diff --git a/scripts/bakemacaroon.sh b/scripts/bakemacaroon.sh new file mode 100755 index 0000000..e35403c --- /dev/null +++ b/scripts/bakemacaroon.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +# This command creates a macaroon containing permissions to call all the +# enpoints lndmanage uses. This is more secure than using an admin macaroon. + +lncli bakemacaroon \ + --save_to lndmanage.macaroon \ + uri:/lnrpc.Lightning/GetInfo \ + uri:/lnrpc.Lightning/GetChanInfo \ + uri:/lnrpc.Lightning/GetNodeInfo \ + uri:/lnrpc.Lightning/DescribeGraph \ + uri:/lnrpc.Lightning/ListChannels \ + uri:/lnrpc.Lightning/FeeReport \ + uri:/lnrpc.Lightning/UpdateChannelPolicy \ + uri:/lnrpc.Lightning/ForwardingHistory \ + uri:/lnrpc.Lightning/ClosedChannels \ + uri:/lnrpc.Lightning/BatchOpenChannel \ + uri:/lnrpc.Lightning/ConnectPeer \ + uri:/walletrpc.WalletKit/ListUnspent \ + uri:/routerrpc.Router/QueryMissionControl From 02f2923069497a98dead0d7f2cbb8c3a6046b10f Mon Sep 17 00:00:00 2001 From: bitromortac Date: Sun, 31 Dec 2023 10:09:14 +0100 Subject: [PATCH 3/5] docker: use readonly macaroon --- docker/Dockerfile | 2 +- docker/README.md | 8 +++++--- docker/_settings.sh | 8 ++++---- docker/home/config_template.ini | 2 +- docker/lndmanage.sh | 6 +++--- 5 files changed, 14 insertions(+), 12 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 35cd03c..b0090cd 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -37,7 +37,7 @@ COPY --from=builder /root/.venv /root/.venv ENV PATH="/root/.venv/bin:$PATH:/root" ENV TLS_CERT_FILE /root/aux/tls.cert -ENV ADMIN_MACAROON_FILE /root/aux/admin.macaroon +ENV MACAROON_FILE /root/aux/readonly.macaroon # copy sources under /root/lndmanage WORKDIR /root/lndmanage diff --git a/docker/README.md b/docker/README.md index a8dbf13..48a4150 100644 --- a/docker/README.md +++ b/docker/README.md @@ -8,19 +8,21 @@ To run `lndmanage` from a docker container: # so $HOME directory is /root # build the container -./build.sh +./build.sh # if you have local lnd node on host machine, point LND_HOME to your actual lnd directory: export LND_HOME=~/.lnd # or alternatively if you have remote lnd node, specify paths to auth files explicitly: # export TLS_CERT_FILE=/path/to/tls.cert -# export ADMIN_MACAROON_FILE=/path/to/admin.macaroon +# export MACAROON_FILE=/path/to/readonly.macaroon # export LND_GRPC_HOST=:10009 +# note that in order to have all features available, you will need to create a +# custom macaroon, see `scripts/bakemacaroon.sh` # look into _settings.sh for more details on container configuration -# run lndmanage from the container: +# run lndmanage from the container: ./lndmanage.sh status # lndmanage cache will be mapped to host folder at ./_volumes/lndmanage-cache diff --git a/docker/_settings.sh b/docker/_settings.sh index 12c2582..7d06dcd 100755 --- a/docker/_settings.sh +++ b/docker/_settings.sh @@ -1,22 +1,22 @@ #!/usr/bin/env bash -# you have two possible ways how to specify ADMIN_MACAROON_FILE and TLS_CERT_FILE +# you have two possible ways how to specify MACAROON_FILE and TLS_CERT_FILE # 1. specify LND_HOME if it is located on your local machine, we use default paths from there -# 2. specify env variables ADMIN_MACAROON_FILE and TLS_CERT_FILE +# 2. specify env variables MACAROON_FILE and TLS_CERT_FILE # also you want to specify LND_GRPC_HOST if your node is remote # other config tweaks have to be done by changing lndmanage/home/config_template.ini # note: docker uses network_mode: host -if [[ -z "$ADMIN_MACAROON_FILE" || -z "$TLS_CERT_FILE" ]]; then +if [[ -z "$MACAROON_FILE" || -z "$TLS_CERT_FILE" ]]; then if [[ -z "$LND_HOME" ]]; then export LND_HOME="$HOME/.lnd" echo "warning: LND_HOME is not set, assuming '$LND_HOME'" fi fi -export ADMIN_MACAROON_FILE=${ADMIN_MACAROON_FILE:-$LND_HOME/data/chain/bitcoin/mainnet/admin.macaroon} +export MACAROON_FILE=${MACAROON_FILE:-$LND_HOME/data/chain/bitcoin/mainnet/readonly.macaroon} export TLS_CERT_FILE=${TLS_CERT_FILE:-$LND_HOME/tls.cert} export LND_GRPC_HOST=${LND_GRPC_HOST:-127.0.0.1:10009} diff --git a/docker/home/config_template.ini b/docker/home/config_template.ini index abade4c..8c5e6f1 100644 --- a/docker/home/config_template.ini +++ b/docker/home/config_template.ini @@ -2,7 +2,7 @@ # see docker/build.sh lnd_grpc_host = ${LND_GRPC_HOST} tls_cert_file = ${TLS_CERT_FILE} -admin_macaroon_file = ${ADMIN_MACAROON_FILE} +macaroon_file = ${MACAROON_FILE} [logging] loglevel = INFO diff --git a/docker/lndmanage.sh b/docker/lndmanage.sh index c4acd11..219a147 100755 --- a/docker/lndmanage.sh +++ b/docker/lndmanage.sh @@ -20,9 +20,9 @@ if [[ ! -e "$LNDMANAGE_AUX_DIR" ]]; then fi LNDMANAGE_AUX_DIR_ABSOLUTE=$(abs_path "$LNDMANAGE_AUX_DIR") -# we use LNDMANAGE_AUX_DIR as ad-hoc volume to pass admin.macaroon and tls.cert into our container +# we use LNDMANAGE_AUX_DIR as ad-hoc volume to pass readonly.macaroon and tls.cert into our container # it is mapped to /root/aux, config_template.ini assumes that -cp "$ADMIN_MACAROON_FILE" "$LNDMANAGE_AUX_DIR/admin.macaroon" +cp "$MACAROON_FILE" "$LNDMANAGE_AUX_DIR/readonly.macaroon" cp "$TLS_CERT_FILE" "$LNDMANAGE_AUX_DIR/tls.cert" if [[ -n "$LNDMANAGE_VERBOSE" ]]; then @@ -36,7 +36,7 @@ exec docker run \ -v "$LNDMANAGE_AUX_DIR_ABSOLUTE:/root/aux" \ -e "LND_GRPC_HOST=${LND_GRPC_HOST}" \ -e "TLS_CERT_FILE=/root/aux/tls.cert" \ - -e "ADMIN_MACAROON_FILE=/root/aux/admin.macaroon" \ + -e "MACAROON_FILE=/root/aux/readonly.macaroon" \ -ti \ lndmanage:local \ run-lndmanage "$@" From d57025cf12aa322be8b5af0c8564a5a9d5218bc3 Mon Sep 17 00:00:00 2001 From: bitromortac Date: Sun, 31 Dec 2023 10:17:47 +0100 Subject: [PATCH 4/5] use correct macaroon in tests --- lndmanage/lib/node.py | 6 ++++-- test/testing_common.py | 23 ++++++++++++----------- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/lndmanage/lib/node.py b/lndmanage/lib/node.py index f1f7fd0..61164a2 100644 --- a/lndmanage/lib/node.py +++ b/lndmanage/lib/node.py @@ -65,7 +65,8 @@ class LndNode: def __init__(self, config_file: Optional[str] = None, lnd_home: Optional[str] = None, - lnd_host: Optional[str] = None, regtest=False): + lnd_host: Optional[str] = None, regtest=False, + use_admin=False): """ :param config_file: path to the config file :param lnd_home: path to lnd home folder @@ -85,12 +86,13 @@ class LndNode: # configure lndmanage home: (TODO: separate into config) # if no lnd_home is given, then use the paths from the config, # else override them with default file paths in lnd_home + macaroon = 'readonly.macaroon' if not use_admin else 'admin.macaroon' if self.lnd_home is not None: self.cert_file_path = os.path.join(self.lnd_home, 'tls.cert') bitcoin_network = 'regtest' if self.regtest else 'mainnet' self.macaroon_file_path = os.path.join( self.lnd_home, 'data/chain/bitcoin/', - bitcoin_network, 'readonly.macaroon') + bitcoin_network, macaroon) if self.lnd_host is None: raise ValueError( 'if lnd_home is given, lnd_host must be given') diff --git a/test/testing_common.py b/test/testing_common.py index 5b33588..f27481a 100644 --- a/test/testing_common.py +++ b/test/testing_common.py @@ -2,6 +2,15 @@ import os import shutil from unittest import TestCase +import logging.config + +from lnregtest.lib.network import Network + +from lndmanage import settings +from lndmanage.lib.node import LndNode + +logger = logging.getLogger() +logger.setLevel(logging.INFO) # testing base folder test_dir = os.path.dirname(os.path.realpath(__file__)) @@ -14,19 +23,11 @@ test_data_dir = os.path.join(test_dir, 'test_data') lndmanage_home = os.path.join(test_data_dir, 'lndmanage') os.makedirs(lndmanage_home, exist_ok=True) -# create empty config and set env var to not trigger error when importing settings +# create empty config and set env var to not trigger error when importing +# settings open(os.path.join(lndmanage_home, 'config.ini'), 'a').close() os.environ.setdefault('LNDMANAGE_HOME', lndmanage_home) -from lnregtest.lib.network import Network - -from lndmanage.lib.node import LndNode - -import logging.config -logger = logging.getLogger() -logger.setLevel(logging.INFO) - -from lndmanage import settings settings.CACHING_RETENTION_MINUTES = 0 # constants for testing @@ -95,7 +96,7 @@ class TestNetwork(TestCase): self.lndnode = LndNode( lnd_home=master_node_data_dir, lnd_host='localhost:' + str(master_node_port), - regtest=True + regtest=True, use_admin=True, ) self.graph_test() From d57e79f40dc01c7c9dc992e8f1b9e519af85e402 Mon Sep 17 00:00:00 2001 From: bitromortac Date: Wed, 27 Dec 2023 12:44:29 +0100 Subject: [PATCH 5/5] version: bump to 0.16.0 --- README.md | 10 +++++----- lndmanage/__init__.py | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index a3de1dc..9d2c193 100644 --- a/README.md +++ b/README.md @@ -401,7 +401,7 @@ If you are running an older version of lnd checkout the according [tag](https://github.com/bitromortac/lndmanage/releases). ### Requirements -Installation of lndmanage requires `>=python3.8`, `lnd v0.15.x`, `python3-venv`. +Installation of lndmanage requires `>=python3.8`, `lnd v0.16.x`, `python3-venv`. #### Optional Requirements Depending on if you want to install from source dependency packages you may @@ -439,7 +439,7 @@ You can install lndmanage via three methods: 1\. Install from repository: ``` $ git clone https://github.com/bitromortac/lndmanage && cd lndmanage -$ git checkout v0.15.0 +$ git checkout v0.16.0 $ python3 -m venv venv $ git verify-commit HEAD $ source venv/bin/activate @@ -452,11 +452,11 @@ $ pip install . * Download `.whl` and `.whl.asc` files * Verify signature and install: ``` -$ gpg --verify lndmanage-0.15.0-py3-none-any.whl.asc lndmanage-0.15.0-py3-none-any.whl +$ gpg --verify lndmanage-0.16.0-py3-none-any.whl.asc lndmanage-0.16.0-py3-none-any.whl $ python3 -m venv venv $ source venv/bin/activate $ pip install --upgrade pip setuptools wheel -$ pip install lndmanage-0.15.0-py3-none-any.whl +$ pip install lndmanage-0.16.0-py3-none-any.whl ``` 3\. Install with pip (deprecated): @@ -479,7 +479,7 @@ You need to set the environment variable `PYTHONIOENCODING` for proper encoding ``` $ git clone https://github.com/bitromortac/lndmanage $ cd lndmanage -$ git checkout v0.15.0 +$ git checkout v0.16.0 $ git verify-commit HEAD $ py -m venv venv $ .\venv\Scripts\activate diff --git a/lndmanage/__init__.py b/lndmanage/__init__.py index a842d05..8911e95 100644 --- a/lndmanage/__init__.py +++ b/lndmanage/__init__.py @@ -1 +1 @@ -__version__ = '0.15.0' +__version__ = '0.16.0'