lndmanage is a command line tool for advanced channel management of an LND node.
See installation instructions.
- Activity reports
report
- Display the node summary
status
info
command: explore info about a channel or node in the graph- Advanced channel listings
listchannels
listchannels rebalance
: list channels for rebalancinglistchannels forwardings
: list forwarding statistics for each channellistchannels hygiene
: information for closing of active channelslistchannels inactive
: information on inactive channels
- Peer listing
listpeers
: aggregated channel statistics - Fee updating
update-fees
: increase revenue and rebalance by fee optimization - Rebalancing command
rebalance
- different rebalancing strategies can be chosen
- a target 'balancedness' can be specified (e.g. to empty the channel)
- Recommendation of good nodes
recommend-nodes
- Batched channel opening
openchannels
- Support of
lncli
DISCLAIMER: This is BETA software, so please be careful (All actions are
executed as a dry run unless you call lndmanage with the --reckless
flag though). No warranty is given.
usage: lndmanage.py [-h] [--loglevel {INFO,DEBUG}] {status,listchannels,rebalance,circle,recommend-nodes,report,info,lncli,openchannels,update-fees} ...
Lightning network daemon channel management tool.
positional arguments:
{status,listchannels,rebalance,circle,recommend-nodes,report,info,lncli,openchannels,update-fees}
status display node status
listchannels lists channels with extended information [see also subcommands with -h]
listpeers lists peers with extended information
rebalance rebalance a channel
recommend-nodes recommends nodes [see also subcommands with -h]
report displays reports of activity on the node
info displays info on channels and nodes
lncli execute lncli
openchannels opens multiple channels with UTXO control
update-fees optimize the fees on your channels to increase revenue and to automatically rebalance
Sometimes it is necessary to get more information about a specific public channel or node. This could be for example trying to figure out what fees are typically charged by a node or to look up its IP address.
With the info command you can enter
$ lndmanage info CHANNEL_ID
or
$ lndmanage info NODE_PUBLIC_KEY
and it will automatically detect whether you are asking for a channel or node info.
Sample output for a channel:
-------- Channel info --------
channel id: CHANIDXXXXXXXXXXXX channel point: CHANPOINTXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:X
capacity: 500000 sat
blockheight: 606273
open since: 2019-10-07 13:31:24
channel age: 139.030000 days
last update: 2020-02-25 06:15:09
-------- Channel partners --------
NODEPUBKEYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX | NODEPUBKEYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
ALIAS 1 | ALIAS 2
base fee: 1000 msat | base fee: 1000 msat
fee rate: 0.000001 sat/sat | fee rate: 0.002500 sat/sat
time lock delta: 40 blocks | time lock delta: 14 blocks
disabled: False | disabled: False
last update: 2020-01-20 13:12:09 | last update: 2020-01-22 10:28:57
Sample output for a node:
-------- Node info --------
NODEPUBKEYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
alias: ALIAS
last update: 2020-02-24 16:45:09
number of channels: 44
total capacity: 33333333 sat
capacity (median): 150000 sat
capacity (mean): 500000 sat
base fee (median): 1000 msat
base fee (mean): 666 msat
fee rate (median): 0.000001 sat/sat
fee rate (mean): 0.002039 sat/sat
-------- Addresses --------
NODEPUBKEYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX@XX.XXX.XXX.XXX:9735
With lndmanage you can get a compact overview of what happened during the last day(s). It will show you forwarding activity (total forwardings, forwarding fees, and forwarding amounts) as well as channel opening and closing events by invoking
$ lndmanage report
Different time intervals can be specified with the --from-days-ago
and
--to-days-ago
flags.
Here is a sample report for one of the subreports. The activity histogram for the time interval is displayed as a one-line histogram, which consists of Braille-like characters.
Report from yyyy-mm-dd hh:mm to yyyy-mm-dd hh:mm
Forwardings:
activity (⣿ represents 8 forwardings):
|⠀⠀⡀⡀⠀⠀⠀⠀⠀⠀⡀⠀⠀⠀⠀⠀⠀⣄⠀⣀⠀⣦⣀⠀⡀⡀⠀⡀⡀⠀⡀⠀⠀⠀⠀⠀⠀⣀⣀⡀⣀⡀⡀⣀⠀⣀⡀⣄|
total forwardings: 37
forwardings per day: 37
channels with most outgoing forwardings:
cidxxxxxxxxxxxxxxx: 10
cidxxxxxxxxxxxxxxx: 6
cidxxxxxxxxxxxxxxx: 4
cidxxxxxxxxxxxxxxx: 3
cidxxxxxxxxxxxxxxx: 3
Channels with depleted liquidity can be rebalanced with channels of saturated liquidity by circular self-payments, that is, a payment leaves from the saturated channel and arrives in the depleted channel. This operation has the cost of paying routing fees. Another often overlooked cost is state accumulation. Rebalancing is a time consuming process where many attemps are needed, because one is often trying to counter the natural flow within the network. This can lead to enlarged databases and therefore overall slower operations and strain on the network, which is why rebalancing should be used sparingly!
Which channels could be rebalanced? Typically, we want to rebalance channels that give us high fee earning expecations [to see those channels, see $ lndmanage listchannels forwardings
, fields fo/w
(fees earned per week) and fi/w
(fees earned by other channels due to incoming payments per week)]. We want to increase local balance in channels with high fo/w
and decrease local balance in channels with high fi/w
. Another use case is to increase/decrease local balance for new channels to see whether there's demand for forwarding. Otherwise it is not helpful to perfectly rebalance a channel 50:50 in most cases.
The workflow to rebalance a channel is as follows:
-
ideally your channels have meaningful fee rates set (see
$ lndmanage update-fees
) -
the counterparty rebalance channels are chosen according to the expected earnings calculated from the fee rate that is set for the to-be-rebalanced channel, i.e., a channel with a low fee rate will send its liquidity to channels with high fee rates, such that this capital can earn fees in the future and vice-versa
-
take a look at all your channels with forwarding activity and select a channel with high
fo/w
orfi/w
, or high absolute values ofub
-
the
ub
field tells you how unbalanced your channel is and in which direction (negative means that the channel has mostly local balance - you may want to decrease the local balance) -
take a
channel_id
ornode_id
you wish to rebalance -
decide on whether you want to increase (positive
--amount
) or decrease your local balance (negative--amount
), chose small amounts (< 1000000 sat) for better success -
if
--amount
is not set, the sign of liquidity change is determined automatically -
do a dry run to see what's waiting for you
$ lndmanage rebalance channel_id/node_id
-
read the output and if everything looks well, then run with the
--reckless
flag -
if you want to disable the fee rate selection of counterparty channels to increase success probability, run with the
--force
flag
A more sophisticated way to see if funds have to be reallocated is to
have a look at the forwarding statistics of, e.g., the last two months
of the individual channels with
$ lndmanage listchannels forwardings --from-days-ago 60 --sort-by='fees'
(here sorted by total fees, but it can be sorted by any column field).
The output will look like:
-------- Description --------
cid channel id
nfwd number of forwardings
age channel age [days]
fees total fees [sat]
f/w total fees per week [sat / week]
flow flow direction (positive is outwards)
ub unbalancedness [-1 ... 1] (0 is 50:50 balanced)
bwd bandwidth demand: capacity / max(mean_in, mean_out)
r action is required
cap channel capacity [sat]
pbf peer base fee [msat]
pfr peer fee rate
annotation channel annotation
alias alias
-------- Channels --------
cid nfwd age fees f/w flow ub bwd r cap pbf pfr alias
xxxxxxxxxxxxxxxxxx 6 103 907 106.950 1.00 0.30 0.00 X 6000000 231 0.000006 abc
xxxxxxxxxxxxxxxxxx 3 82 300 35.374 -0.08 0.74 0.70 1000000 1000 0.000001 def
xxxxxxxxxxxxxxxxxx 4 32 216 25.461 0.42 0.38 0.17 X 6000000 1003 0.000003 ghi
...
The update-fees
command lets you dynamically update the fee rates and base fees on your
channels. It analyzes the outward (fee-earning) forwardings that happened on them and lowers
or increases fees incrementally based on the demand. The minimal and maximal fee rate boundaries
are configurable (see update-fees -h
). The fee optimization will enforce that fee rates
are not lowered, when the channel has no outbound liquidity, it economically enforces a
buffer for excess demand times.
The command will not set new fees unless the user answers with yes
after the statistics output.
Example output for a channel with excess demand:
>>> Fee optimization for node XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX (node alias):
Channels with peer: 1, total capacity: 5000000, total local balance: 1033113
Outward forwarded amount: 1521253 (rate 217322 / target rate 14286)
Number of outward forwardings: 1
Fee rate change: 0.000150 -> 0.000225 (factor 1.500)
Base fee change: 0 -> 0 (factor 0.750)
> Statistics for channel XXXXXXXXXXXXXXXXXX:
ub: 0.59, flow: 0.26, fees: 226.666 sat, cap: 5000000 sat, lb: 1033113 sat, nfwd: 2, in: 895518 sat, out: 1521253 sat.
One can see that the channel routed more than the target of 14286 sat/day, so the fee rate is increased by a factor of 1.5.
Example output for a channel with no demand:
>>> Fee optimization for node XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX (node alias):
Channels with peer: 1, total capacity: 5000000, total local balance: 3134892
Outward forwarded amount: 0 (rate 0 / target rate 14286)
Number of outward forwardings: 0
Fee rate change: 0.000149 -> 0.000106 (factor 0.707)
Base fee change: 0 -> 0 (factor 0.750)
> Statistics for channel XXXXXXXXXXXXXXXXXX:
ub: -0.25, flow: 0.00, fees: 0.000 sat, cap: 5000000 sat, lb: 3134892 sat, nfwd: 0, in: 0 sat, out: 0 sat.
There was no demand on that channel, so the fee rate was decreased by a factor of 0.707.
Example for an exhausted channel:
>>> Fee optimization for node XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX (node alias)
Channels with peer: 1, total capacity: 2000000, total local balance: 20810
Outward forwarded amount: 0 (rate 0 / target rate 14286)
Number of outward forwardings: 0
Fee rate change: 0.000150 -> 0.000157 (factor 1.048)
Base fee change: 0 -> 0 (factor 0.750)
> Statistics for channel XXXXXXXXXXXXXXXXXX:
ub: 0.98, flow: 0.00, fees: 0.000 sat, cap: 2000000 sat, lb: 20810 sat, nfwd: 0, in: 0 sat, out: 0 sat.
This channel is exhausted (it only has 20810 sat left in it, or ub=0.98). Even though there was no demand for this channel, the fee rate is not lowered, but kept roughly constant (increased a bit by factor 1.048) in the hope it will be filled again by an incoming forwarding event.
The target for how much a channel should route per day can be set via the
--target-forwarding-amount-sat
config parameter. This value has direct influence on
the revenue, but it is unknown beforehand and every node operator has to tune it.
A reasonable value could be half of the daily routed amount of the best-income
channel (see lndmanage listchannels forwardings
). Future work will focus on setting
the target amount automatically on a per channel basis. After each optimization step
forwarding statistics are collected in a json file, to use the data in the future to
model the fee-demand curve. Please report if the default parameter is way too off for you.
Channels can be exempt from fee updates via the [excluded-channels-fee-opt]
config
section, see config example.
The update-fees
command is meant to be run periodically. A weekly interval is
recommended to not put too much strain on the network, which also averages out weekly
patterns and makes the gossip propagate also to nodes that are not always
online like mobile phones.
A convenient way to run this command automatically (every Sunday) is via a cronjob:
$ crontab -e
# m h dom mon dow command
0 0 * * Sun lndmanage update-fees --reckless --from-days-ago 7
If a node was bootstrapped or one is unsure which initial fees to apply, it is
recommended to apply high inital fee rates. This can be accomplished by
$ lndmanage update-fees --init
. If the node has done already some forwardings,
one can immediately follow with a $ lndmanage update-fees --from-days-ago 30
and
the fees will adjust downwards or upwards depending on the historic traffic. In order
for opened channels to not start with a very low fee setting (and thus to prevent
immediate depletion), it is recommended to set a default high fee rate for channel
opening in the lnd config:
bitcoin.feerate=2500
Inactive channels (Zombie channels)
lock up capital, which can be used elsewhere.
In order to close those channels it is useful to take a look
at the inactive channels with $ lndmanage listchannels inactive
.
You will get an output like:
-------- Description --------
cid channel id
lupp last update time by peer [days ago]
priv channel is private
ini true if we opened channel
age channel age [days]
ub unbalancedness [-1 ... 1] (0 is 50:50 balanced)
cap channel capacity [sat]
lb local balance [sat]
rb remote balance [sat]
sr/w sent and received per week [sat]
annotation channel annotation
alias alias
-------- Channels --------
cid lupp priv ini age ub cap lb rb sr/w alias
xxxxxxxxxxxxxxxxxx 66 ✗ ✓ 71 0.03 2000000 10000 100 0 abc
xxxxxxxxxxxxxxxxxx 20 ✗ ✗ 113 -0.23 40000 0 0 0 def
xxxxxxxxxxxxxxxxxx 19 ✓ ✗ 21 1.00 1200000 1000 0 0 ghi
...
Channels, which were updated a long time ago (lupp
) are likely to be
inactive in the future and may be closed. Be aware, that if you are the initiator
of the channel, you have to pay a hefty fee for the force closing.
As well as inactive channels, active channels can lock up capital that is
better directed towards other nodes. In order to facilitate the hard
decision whether a channel should be closed, one can have a look at the
$ lndmanage listchannels hygiene
command output, which will display relevant
data of the last 60 days:
-------- Description --------
cid channel id
age channel age [days]
nfwd number of forwardings
f/w total fees per week [sat / week]
ulr ratio of uptime to lifetime of channel [0 ... 1]
lb local balance [sat]
cap channel capacity [sat]
pbf peer base fee [msat]
pfr peer fee rate
annotation channel annotation
alias alias
-------- Channels --------
cid age nfwd f/w ulr lb cap pbf pfr alias
xxxxxxxxxxxxxxxxxx 315 0 0.00 0.20 100 91000 1000 0.000001 abc
xxxxxxxxxxxxxxxxxx 221 0 0.00 0.80 0 400000 1000 0.000001 def
xxxxxxxxxxxxxxxxxx 36 0 0.00 0.99 0 200000 1000 0.000001 ghi
xxxxxxxxxxxxxxxxxx 24 5 0.20 1.00 100000 4000000 500 0.000001 jkl
xxxxxxxxxxxxxxxxxx 117 10 1.10 1.00 30000 500000 1000 0.000001 mno
You can base your decision on the number of forwardings nfwd
and the fees
collected per week f/w
. If those numbers are low and the local balance lb
is high and the channel already had enough time (age
) to prove itself, you
may want to consider closing the channel. Another way to judge the reliability
of the channel is to look at the proportion the channel stayed active when
your node was active, given by the ulr
column.
The command $ lndmanage listpeers
will show aggregated statistics for channels. This
is important if one has for example a public channel (for route advertisement), but
other channels are kept private. Care must be taken that always at least a single channel
is public, which is indicated by the maximum public channel capacity
. listpeers
shows
statistics such as total incoming and outgoing forwarding amounts as well as total local
and remote balance. Subcommands listpeers in
or listpeers out
can be used to sort by
incoming or outgoing traffic.
Which nodes best to connect to in the Lightning Network is ongoing research. This also depends on your personal use case, whether you are a paying user, a routing node operator or a service provider (or subsets of those). Therefore we need to empirically test, what good nodes mean to us. lndmanage gives you a few options to chose nodes from the network based on several heuristics:
recommend-nodes good-old
: Based on historic forwardings of closed channels, a list of nodes is compiled with which your node has already had a good relationship. Due to that relationship, good interaction with that node in the future is likely.recommend-nodes flow-analysis
: If your node has already routed payments, you can use this information to your favor. If you want to improve your position in the Lightning Network for routing, you may want to look for need of inbound liquidity. This can be achieved by estimating the probability where the payments you routed were ending up. If you connect to those nodes directly you bypass other routing nodes.recommend-nodes external-source
: This command lets you access text-based lists of nodes, which are associated with economic activity. You can provide a URL, which is parsed for node public keys and suggests nodes to connect to (defaults to the list of lightning networkstores). Another example of the command using 'bos-scores' is$ lndmanage recommend-nodes external-source --source https://nodes.lightning.computer/availability/v1/btc.json
.recommend-nodes channel-openings
: When lightning nodes of new services are bootstrapped by opening a bunch of channels at the same time, we can detect this. Typically, a node with a large number of channel fluctuation signals economic activity. As the newly opened channels will predominantly be of outbound type, the node will have a large demand for inbound liquidity, which is something you want to exploit as a routing node.recommend-nodes second-neighbors
: One way of improving the position of the node in the network is to get connected to as many as possible other nodes with a least number of additional hops. With thesecond-neighbors
command you can get a list of nodes that would give you the most new second neighbors, if you would open up a channel with.
lndmanage supports a channel annotation functionality. This serves for
remembering why a certain channel was opened. By adding the funding
transaction id or channel id to the config file ~/.lndmanage/config.ini
under the annotations
section (as specified in
config_sample.ini
), annotations
can be saved. These annotations will then appear in the listchannels
views.
lndmanage supports batched channel opening support using LND's internal wallet. With this command you can specify node pubkeys, amounts, and have coin control to avoid change creation. Reserves for anchor commitments are respected or created automatically.
The openchannels
command can operate in several ways. You only specify
the node pubkeys as a minimal input and the tool will automatically connect
to the nodes in the channel opening process. You may also specify a list
of UTXOs which are used as the budget. The individual channel capacities
can be given either as absolute values, or as relative values
(they will be rescaled to the sum of UTXOs), or you can give a total amount,
which will be distributed over the new channels.
Anchor commitments introduce some UX issues with reserving UTXOs that are needed to confirm commitment transactions in time via child-pays-for-parent. This is why you won't be able to spend down all funds on newer versions of LND. lndmanage recognizes this case and will not touch small UTXOs that are suitable for reserves, or it will create them.
lndmanage supports the native command line interface of lnd
in interactive mode.
This achieves the goal of having to only open one terminal window for node
management and enables an easy way to run lncli
from remote machines. In
interactive mode lncli
is available as it would be via command line, e.g.:
$ lndmangage lncli getinfo
The json text output you get from lncli
is syntax highlighted. To enable
lncli
support, lndmanage needs to find the lncli
executable on the path, or
in the ~/.lndmanage
home folder (or environment variable LNDMANAGE_HOME
).
To get the lncli
binary, copy it over from your $GOPATH/bin/lncli
, compile it
yourself, or download one of the official releases.
If lndmanage runs on the same host as lnd
you typically don't have to do
anything. To check if it's working you should see
Enabled lncli: using /path/to/lncli
and be able to access the lncli
option.
lndmanage will be developed in lockstep with lnd and tagged accordingly. If you are running an older version of lnd checkout the according tag.
Installation of lndmanage requires >=python3.8
, lnd v0.14.x
, python3-venv
.
Depending on if you want to install from source dependency packages you may
need gcc
, g++
, python3-dev(el)
.
Some commands will only work correctly if lnd is built with the routerrpc
.
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.
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.
You can install lndmanage via two methods:
1. Install with pip (recommended):
$ python3 -m venv venv
$ source venv/bin/activate
$ pip install --upgrade pip setuptools wheel
$ python3 -m pip install lndmanage
2. Install from source:
$ git clone https://github.com/bitromortac/lndmanage
$ cd lndmanage
$ python3 -m venv venv
$ source venv/bin/activate
$ pip install --upgrade pip setuptools wheel
$ pip install .
Install python3, git, and visual studio build tools.
You need to set the environment variable PYTHONIOENCODING
for proper encoding to:
$env:PYTHONIOENCODING="UTF-8"
1. Install with pip (recommended):
$ py -m venv venv
$ .\venv\Scripts\activate
$ pip install --upgrade pip setuptools wheel
$ python -m pip install lndmanage
2. Install from source:
$ git clone https://github.com/bitromortac/lndmanage
$ cd lndmanage
$ py -m venv venv
$ .\venv\Scripts\activate
$ pip install --upgrade pip setuptools wheel
$ pip install .
When starting lndmanage for the first time, it will create a runtime folder
/home/user/.lndmanage
, where the configuration config.ini
and log files
reside. This folder location can be overwritten by setting an environment
variable LNDMANAGE_HOME
. If you run this tool from a remote host to the lnd
host, you need to configure config.ini
.
The installation process created an executable lndmanage
, which will
only be available if the created python environment is active (your prompt
should have an (venv)
in front):
$ source venv/bin/activate
then run
(venv) $ lndmanage status
If it works, you should see the node status.
Running lndmanage interactively (recommended)
lndmanage supports an interactive mode with command history. The interactive mode has the advantage that the network graph has to be read into memory only once, giving a much faster execution time for subsequent command invocations.
Interactive mode is started by calling lndmanage without arguments:
$ lndmanage
Running in interactive mode. You can type 'help' or 'exit'.
$ lndmanage listchannels forwardings
<output>
$ lndmanage exit
Commands that can be entered are the ones you would give as arguments.
Requirements are an installation of lnregtest
and links to bitcoind, bitcoin-cli, lnd, and lncli in the test/bin
folder.
Tests can be run with
python3 -m unittest discover test
from the root folder.
Note that this repository ships prebuilt gRPC interfaces to communicate with LND.
These libraries (located in lndmanage/grpc_compiled
) contain code that is hard
to review and should not be trusted. In order to check that the libraries indeed
can be reproduced from the LND repository, one can run the build_grpc.sh
script and observe differences via git.
If you prefer to run lndmanage
from a docker container, cd docker
and follow the README
there.