Lightning client

Lightning client

We set up LND (opens in a new tab), the Lightning Network Daemon by Lightning Labs (opens in a new tab).

Preparations

Create the lnd user/group

$SU addgroup -S lnd
$SU adduser \
    -S \
    -D \
    -H \
    -h /var/lib/lnd \
    -s /sbin/nologin \
    -G lnd \
    -g lnd \
    lnd

Add lnd user to the bitcoin group

$SU adduser lnd bitcoin

Add the user satoshi to the group lnd as well

$SU adduser satoshi lnd

Add the user lnd to the group tor

This allow the user lnd to use the control port and configure Tor directly

$SU adduser lnd tor

Create a symbolic link lnd to satoshi home

ln -s /var/lib/lnd "$HOME/.lnd"

Installation

The installation of LND is straight-forward, but the application is quite powerful and capable of things not explained here. Check out their GitHub repository (opens in a new tab) for a wealth of information about their open-source project and Lightning in general.

Download binaries

We'll download, verify and install LND.

  • Download the application and the checksums file (.txt) with its signature (.sig) and timestamp (.ots)
cd /tmp
VERSION=0.17.4-beta
wget https://github.com/lightningnetwork/lnd/releases/download/v$VERSION/lnd-linux-amd64-v$VERSION.tar.gz \
    https://github.com/lightningnetwork/lnd/releases/download/v$VERSION/manifest-v$VERSION.txt \
    https://github.com/lightningnetwork/lnd/releases/download/v$VERSION/manifest-roasbeef-v$VERSION.sig \
    https://github.com/lightningnetwork/lnd/releases/download/v$VERSION/manifest-roasbeef-v$VERSION.sig.ots \
    https://raw.githubusercontent.com/lightningnetwork/lnd/master/sample-lnd.conf

Checksum check

  • Verify the signed checksum against the actual checksum of your download
grep lnd-linux-amd64-v$VERSION.tar.gz manifest-v$VERSION.txt | sha256sum -c
output
lnd-linux-amd64-v0.17.4-beta.tar.gz: OK

Signature check

Now that we've verified the integrity of the downloaded binary, we need to check the authenticity of the manifest file we just used, starting with its signature.

  • Install gnupg
$SU apk add gnupg
  • Get the public key from the LND developer who signed the manifest file; and add it to your GPG keyring
wget -qO- \
    https://raw.githubusercontent.com/lightningnetwork/lnd/master/scripts/keys/roasbeef.asc |\
    gpg --import
output
[...]
gpg: key 372CBD7633C61696: public key "Olaoluwa Osuntokun <laolu32@gmail.com>" imported
[...]
  • Verify the signature of the text file containing the checksums for the application
gpg --verify manifest-roasbeef-v$VERSION.sig manifest-v$VERSION.txt
output
gpg: Signature made Tue Feb  6 20:39:45 2024 CET
gpg:                using RSA key 60A1FA7DA5BFF08BDCBBE7903BBD59E99B280306
gpg: Good signature from "Olaoluwa Osuntokun <laolu32@gmail.com>" [unknown]
gpg: WARNING: This key is not certified with a trusted signature!
gpg:          There is no indication that the signature belongs to the owner.
Primary key fingerprint: E4D8 5299 674B 2D31 FAA1  892E 372C BD76 33C6 1696
     Subkey fingerprint: 60A1 FA7D A5BF F08B DCBB  E790 3BBD 59E9 9B28 0306

Timestamp check

We can also check that the manifest file was in existence around the time of the release using its timestamp.

  • Let's verify the timestamp of the file matches the release date.
ots --no-cache verify manifest-roasbeef-v$VERSION.sig.ots -f manifest-roasbeef-v$VERSION.sig
output
[...]
Success! Bitcoin block 829257 attests existence as of 2024-02-06 CET

Installation

Having verified the integrity and authenticity of the release binary, we can safely proceed to install it!

  • Install LND
tar xzf lnd-linux-amd64-v$VERSION.tar.gz
$SU install -m 0755 -o root -g root -t /usr/bin lnd-linux-amd64-v$VERSION/*
$SU install -D -m 0660 -o lnd -g lnd ./sample-lnd.conf /etc/lnd/lnd.conf
lnd --version
output
lnd version 0.17.4-beta commit=v0.17.4-beta

Configuration

  • Modify/uncomment these lines. Save and exit.
$EDITOR /etc/lnd/lnd.conf
/etc/lnd/lnd.conf
[...]
tlsautorefresh=true
[...]
tlsdisableautofill=true
[...]
listen=127.0.0.1
[...]
debuglevel=info
[...]
maxpendingchannels=5
[...]
wallet-unlock-password-file=/etc/lnd/passwd.txt
[...]
wallet-unlock-allow-create=true
[...]
minchansize=100000
[...]
coop-close-target-confs=24
[...]
ignore-historical-gossip-filters=1
[...]
stagger-initial-reconnect=true
[...]
accept-keysend=true
[...]
accept-amp=true
[...]
gc-canceled-invoices-on-startup=true
[...]
gc-canceled-invoices-on-the-fly=true
[...]
alias=YOUR_FANCY_ALIAS
[...]
bitcoin.active=true
[...]
bitcoin.chaindir=/var/lib/lnd/chain/bitcoin
[...]
bitcoin.mainnet=true
[...]
bitcoin.node=bitcoind
[...]
bitcoin.basefee=1000
[...]
bitcoin.feerate=1
[...]
bitcoind.dir=/var/lib/bitcoind
[...]
bitcoind.config=/etc/bitcoin/bitcoin.conf
[...]
bitcoind.rpccookie=/var/lib/bitcoind/.cookie
[...]
bitcoind.zmqpubrawblock=tcp://127.0.0.1:28332
[...]
bitcoind.zmqpubrawtx=tcp://127.0.0.1:28333
[...]
tor.active=true
[...]
tor.streamisolation=true
[...]
tor.v3=true
[...]
watchtower.active=true
[...]
wtclient.active=true
[...]
protocol.wumbo-channels=true
[...]
protocol.simple-taproot-chans=true
[...]
db.bolt.auto-compact=true
[...]
db.bolt.auto-compact-min-age=168h
[...]
Slow device mode
/etc/lnd/lnd.conf
[...]
; watchtower.active=true
[...]
💡

Check the LND sample config (opens in a new tab) on web browser, and visit the Lightning Node Management (opens in a new tab) site by Openoms to learn more.

Autostart on boot

Now, let's set up LND to start automatically on system startup.

  • Create LND init.d unit with the following content. Save and exit.
$SU $EDITOR /etc/init.d/lnd
/etc/init.d/lnd
#!/sbin/openrc-run
 
: ${LND_CONFIGFILE:=/etc/lnd/lnd.conf}
: ${LND_PASSWDFILE:=/etc/lnd/passwd.txt}
: ${LND_DATADIR:=/var/lib/lnd}
: ${LND_LOGDIR:=/var/log/lnd}
: ${LND_USER:=lnd}
: ${LND_GROUP:=lnd}
: ${LND_BIN:=/usr/bin/lnd}
: ${LND_OPTS=${LND_OPTS}}
: ${LND_SIGTERM_TIMEOUT:=600}
 
name="LND"
description="Lightning Network Daemon"
 
required_files="${LND_CONFIGFILE}"
pidfile="/run/${SVCNAME}.pid"
retry="${LND_SIGTERM_TIMEOUT}"
 
command="${LND_BIN}"
command_args="--lnddir=${LND_DATADIR}
              --configfile=${LND_CONFIGFILE}
              ${LND_OPTS}"
command_user="${LND_USER}:${LND_GROUP}"
command_background="true"
 
depend() {
    need bitcoind
    checkdepend tor.active tor
}
 
checkdepend() {
    if grep -qs "^${1}=" "${LND_CONFIGFILE}"; then
        need "${2:-$1}"
    fi
}
 
start_pre() {
    checkpath --file      --mode 0660 --owner "${command_user}" "${LND_CONFIGFILE}"
    checkpath --file      --mode 0600 --owner "${command_user}" "${LND_PASSWDFILE}"
    checkpath --directory --mode 0750 --owner "${command_user}" "${LND_DATADIR}"
    checkpath --directory --mode 0755 --owner "${command_user}" "${LND_LOGDIR}"
 
    [ -e "${LND_DATADIR}/logs" ] || ln -s "${LND_LOGDIR}" "${LND_DATADIR}/logs"
 
    checkconfig
}
 
start_post() {
    chmod -R u=rwX,g=rX,o= "${LND_DATADIR}" "${LND_LOGDIR}"
}
 
checkconfig() {
    if ! grep -qs '^bitcoind.rpccookie=' "${LND_CONFIGFILE}"
    then
        eerror ""
        eerror "ERROR: You must set a bitcoind.rpccookie= path to run LND."
        eerror "The setting must appear in ${LND_CONFIGFILE}"
        eerror ""
        return 1
    fi
}
  • Enable execution permission
$SU chmod +x /etc/init.d/lnd

LND in action

Wallet password

LND includes a Bitcoin wallet that manages your on-chain and Lightning coins. It is password protected and must be unlocked when LND starts. This creates the dilemma that you either manually unlock LND after each restart of your computer, or you store the password somewhere on the node.

For this initial setup, we choose the easy route: we store the password in a file that allows LND to unlock the wallet automatically. This is not the most secure setup, but you can improve it later if you want. To give some perspective: other Lightning implementations like c-lightning or Eclair don't even have a password.

  • Create a text file and enter your LND wallet password [C]. Save and exit.
$EDITOR /etc/lnd/passwd.txt

To improve the security of your wallet, check out these more advanced methods:

Enable and start LND

$SU rc-update add lnd
$SU rc-service lnd start
 

Wallet setup

Once LND is started, the process waits for us to create the integrated Bitcoin wallet.

  • Create the LND wallet
lncli create
  • Enter your password [C] as wallet password (it must be exactly the same you stored in passwd.txt). To create a a new wallet, select n when asked if you have an existing cipher seed. Just press enter if asked about an additional seed passphrase, unless you know what you're doing. A new cipher seed consisting of 24 words is created.
output
Input wallet password:
Confirm password:
 
Do you have an existing cipher seed mnemonic or extended master root key you want to use?
Enter 'y' to use an existing cipher seed mnemonic, 'x' to use an extended master root key
or 'n' to create a new seed (Enter y/x/n): n
 
Your cipher seed can optionally be encrypted.
Input your passphrase if you wish to encrypt it (or press enter to proceed without a cipher seed passphrase):
 
Generating fresh cipher seed...
 
!!!YOU MUST WRITE DOWN THIS SEED TO BE ABLE TO RESTORE THE WALLET!!!

---------------BEGIN LND CIPHER SEED---------------
 1. [REDACTED]   2. [REDACTED]   3. [REDACTED]   4. [REDACTED]
 5. [REDACTED]   6. [REDACTED]   7. [REDACTED]   8. [REDACTED]
 9. [REDACTED]  10. [REDACTED]  11. [REDACTED]  12. [REDACTED]
13. [REDACTED]  14. [REDACTED]  15. [REDACTED]  16. [REDACTED]
17. [REDACTED]  18. [REDACTED]  19. [REDACTED]  20. [REDACTED]
21. [REDACTED]  22. [REDACTED]  23. [REDACTED]  24. [REDACTED]
---------------END LND CIPHER SEED-----------------
 
!!!YOU MUST WRITE DOWN THIS SEED TO BE ABLE TO RESTORE THE WALLET!!!
 
lnd successfully initialized!

These 24 words is all that you need to restore the Bitcoin on-chain wallet. The current state of your channels, however, cannot be recreated from this seed. For this, the Static Channel Backup stored at /var/lib/lnd/data/chain/**/**/channel.backup is updated continuously.

⚠️

This information must be kept secret at all times.

  • Write these 24 words down manually on a piece of paper and store it in a safe place.

You can use a simple piece of paper, write them on the custom themed SeedSigner backup card (opens in a new tab), or even stamp the seed words into metal (opens in a new tab). This piece of paper is all an attacker needs to completely empty your on-chain wallet!

Do not store it on a computer.

Do not take a picture with your mobile phone.

This information should never be stored anywhere in digital form.

Restart LND

$SU rc-service lnd restart
  • Check the log to see LND output. Exit with Ctrl-C
tail -f /var/log/lnd/**/**/lnd.log

🎉 Now your Lightning node is ready. This is also the point of no return. Up until now, you can just start over. Once you send real bitcoin to your MicroBolt, you have "skin in the game".

Funding your Lightning node
  • Generate a new Bitcoin address (p2tr = taproot) to receive funds on-chain and send a small amount of Bitcoin to it from any wallet of your choice.

newaddress (opens in a new tab)

lncli newaddress p2tr
example output
{
    "address": "bc1...[REDACTED]..."
}
  • Check your LND wallet balance

walletbalance (opens in a new tab)

lncli walletbalance
example output
{
    "total_balance": "0",
    "confirmed_balance": "0",
    "unconfirmed_balance": "0",
    "locked_balance": "0",
    "reserved_balance_anchor_chan": "0",
    "account_balance": {
        "default": {
            "confirmed_balance": "0",
            "unconfirmed_balance": "0"
        }
    }
}

As soon as your funding transaction is mined (1 confirmation), LND will show its amount as "confirmed_balance".

💡

If you want to open a few channels, you might want to send a few transactions. If you have only one UTXO, you need to wait for the change to return to your wallet after every new channel opening.

Opening channels

Although LND features an optional "autopilot", we manually open some channels.

We recommend to go on Amboss.Space (opens in a new tab) or 1ML.com (opens in a new tab) and look for a mix of big and small nodes with decent Node Ranks. Another great way to find peers to collaboratively set up channels is LightningNetwork+ (opens in a new tab).

To connect to a remote node, you need its URI that looks like <pubkey>@host:

  • the <pubkey> is just a long hexadecimal number, like 03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f
  • the host can be a domain name, an ip address or a Tor onion address, followed by the port number (usually :9735)

Just grab the whole URI above the big QR code and use it as follows (we will use the ACINQ node as an example):

  • Connect to the remote node, with the full URI.

connect (opens in a new tab)

lncli connect 03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f@34.239.230.56:9735
  • Open a channel using the <pubkey> only (i.e., the part of the URI before the @) and the channel capacity in satoshis.

openchannel (opens in a new tab)

One Bitcoin equals 100 million satoshis. The command has a built-in fee estimator, but to avoid overpaying fees, you can manually control the fees for the funding transaction by using the sat_per_vbyte argument as follows (to select the appropriate fee, in sats/vB, check mempool.space (opens in a new tab))

lncli openchannel --sat_per_vbyte 8 03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f 100000 0
  • Check your funds, both in the on-chain wallet and the channel balances.

walletbalance (opens in a new tab)

lncli walletbalance

channelbalance (opens in a new tab)

lncli channelbalance
  • List active channels. Once the channel funding transaction has been mined and gained enough confirmations, your channel is fully operational. That can take an hour or more.

listchannels (opens in a new tab)

lncli listchannels
  • Make a Lightning payment. By default, these work with invoices, so when you buy something or want to send money, you need to get an invoice first. However, you can also pay without requesting an invoice as long the receiving node supports the keysend or amp feature!

To try, why not send me 21 satoshis! You simply need to input my node pukey Stadicus node (opens in a new tab), the amount in satoshis and add the --keysend flag.

lncli sendpayment --dest 02acd93e3352fd59066ca3f23e8865de1926301e8be03c6a52f0f7e43533fe9888 --amt 21 --keysend
Adding watchtowers

Lightning channels need to be monitored to prevent malicious behavior by your channel peers. If your MicroBolt goes down for a longer period of time, for instance due to a hardware problem, a node on the other side of one of your channels might try to close the channel with an earlier channel balance that is better for them.

Watchtowers are other Lightning nodes that can monitor your channels for you. If they detect such bad behavior, they can react on your behalf, and send a punishing transaction to close this channel. In this case, all channel funds will be sent to your LND on-chain wallet.

A watchtower can only send such a punishing transaction to your wallet, so you don't have to trust them. It's good practice to add a few watchtowers, just to be on the safe side.

lncli wtclient add 023bad37e5795654cecc69b43599da8bd5789ac633c098253f60494bde602b60bf@iiu4epqzm6cydqhezueenccjlyzrqeruntlzbx47mlmdgfwgtrll66qd.onion:9911
  • Check if the watchtower is active and list your towers
lncli wtclient towers
output
{
    "towers": [
        {
            "pubkey": "023bad37e5795654cecc69b43599da8bd5789ac633c098253f60494bde602b60bf",
            "addresses": [
                "iiu4epqzm6cydqhezueenccjlyzrqeruntlzbx47mlmdgfwgtrll66qd.onion:9911"
            ],
            "active_session_candidate": true,
            "num_sessions": 0,
            "sessions": [],
            "session_info": [
                {
                    "active_session_candidate": true,
                    "num_sessions": 0,
                    "sessions": [],
                    "policy_type": "ANCHOR"
                },
                {
                    "active_session_candidate": true,
                    "num_sessions": 0,
                    "sessions": [],
                    "policy_type": "LEGACY"
                }
            ]
        }
    ]
}
lncli wtclient remove <pubkey>
Watchtower server
💡
Don't use with Slow device mode configuration

Same as you can connect as a watchtower client to other watchtower servers, you could give the same service running an altruist watchtower server. This was previously activated in the lnd.conf, and you can see the information about it by typing the following command and sharing it with your peers.

lncli tower info
output
{
    "pubkey": "02929aec329a822516a0c5c51f31e7f9b371192a43bf8b6473039915174368a928",
    "listeners": [
        "[::]:9911"
    ],
    "uris": [
        "02929aec329a822516a0c5c51f31e7f9b371192a43bf8b6473039915174368a928@ypeqsjqgafod2772ycabbv762o3blavfkmn3unxdqdnofltyxcendfqd.onion:9911"
    ]
}
More basic useful commands

A quick reference with common commands to play around with:

  • List all arguments for the CLI (command line interface)
lncli
  • Get help for a specific command
lncli help [COMMAND]
lncli getinfo
lncli listpeers
lncli pendingchannels
lncli listchannels
lncli decodepayreq [INVOICE]
  • Pay an invoice:
lncli payinvoice [INVOICE]
lncli sendpayment --amp --fee_limit 1 --dest=<node_pubkey> --final_cltv_delta=144 --amt=<amount_in_sats>
lncli listpayments
lncli addinvoice [AMOUNT_IN_SATOSHIS]
lncli listinvoices
  • To close a channel, you need the following two arguments that can be determined with listchannels and are listed as "channel_point": FUNDING_TXID:OUTPUT_INDEX closechannel (opens in a new tab)
lncli listchannels
lncli closechannel --sat_per_vbyte <fee> [FUNDING_TXID] [OUTPUT_INDEX]
  • To force close a channel (if your peer is offline or not cooperative), use --force
lncli closechannel --force [FUNDING_TXID] [OUTPUT_INDEX]

For the future: upgrade LND

Upgrading LND can lead to a number of issues. Always read the LND release notes (opens in a new tab) completely to understand the changes. These also cover a lot of additional topics and many new features not mentioned here.

  • Check your lnd version
lnd --version
  • Stop the LND service
$SU rc-service lnd stop
  • Download, verify and install the latest LND binaries as described in the LND section of this guide.

  • Restart the services with the new configuration

$SU rc-service lnd restart