Merge pull request #152 from bitromortac/2312-macaroon

default to readonly macaroon, version bump
This commit is contained in:
bitromortac 2024-01-06 19:07:28 +01:00 committed by GitHub
commit 1aa45a7d1b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 123 additions and 272 deletions

View file

@ -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
@ -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
@ -435,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
@ -448,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):
@ -475,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

View file

@ -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

View file

@ -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=<remoteip>: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

View file

@ -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}

View file

@ -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

View file

@ -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 "$@"

View file

@ -1 +1 @@
__version__ = '0.15.0'
__version__ = '0.16.0'

View file

@ -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')

View file

@ -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

View file

@ -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())
@ -73,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
@ -93,20 +86,22 @@ 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, 'admin.macaroon')
bitcoin_network, 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']
)
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']
@ -127,7 +122,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)
@ -140,8 +135,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 +197,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 +293,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 +389,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 +411,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 +537,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 +590,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 +602,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 +628,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 +654,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)

View file

@ -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
# 635263839283742663=ignore

20
scripts/bakemacaroon.sh Executable file
View file

@ -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

View file

@ -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()