Lightning client

Lightning client

We set up LND, the Lightning Network Daemon by Lightning Labs.

Table of contents

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 for a wealth of information about their open-source project and Lightning in general.

Download

We’ll download, verify and install LND.

  • As user “admin”, download the application and the checksums file (.txt) with its signature (.sig) and timestamp (.ots)

    $

    cd

    /tmp

    $

    wget https://github.com/lightningnetwork/lnd/releases/download/v0.16.0-beta/lnd-linux-arm64-v0.16.0-beta.tar.gz

    $

    wget https://github.com/lightningnetwork/lnd/releases/download/v0.16.0-beta/manifest-v0.16.0-beta.txt

    $

    wget https://github.com/lightningnetwork/lnd/releases/download/v0.16.0-beta/manifest-guggero-v0.16.0-beta.sig

    $

    wget https://github.com/lightningnetwork/lnd/releases/download/v0.16.0-beta/manifest-guggero-v0.16.0-beta.sig.ots

Checksum check

  • Verify the signed checksum against the actual checksum of your download

    $

    sha256sum

    --check

    manifest-v0.16.0-beta.txt

    --ignore-missing

    >

    lnd-linux-arm64-v0.16.0-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.

  • Get the public key from the LND developer who signed the manifest file; and add it to your GPG keyring

    $

    curl https://raw.githubusercontent.com/lightningnetwork/lnd/master/scripts/keys/guggero.asc | gpg

    --import

    >

    ...

    >

    gpg: key 8E4256593F177720:

    "Oliver Gugger <[email protected]>"

    1 new signature

    >

    ...
  • Verify the signature of the text file containing the checksums for the application

    $

    gpg

    --verify

    manifest-guggero-v0.16.0-beta.sig manifest-v0.16.0-beta.txt

    >

    gpg: using RSA key F4FC70F07310028424EFC20A8E4256593F177720

    >

    gpg: Good signature from

    "Oliver Gugger <[email protected]>"

    [

    unknown]

    >

    Primary key fingerprint: F4FC 70F0 7310 0284 24EF C20A 8E42 5659 3F17 7720

    >

    [

    ...]

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 verify manifest-guggero-v0.16.0-beta.sig.ots

    -f

    manifest-guggero-v0.16.0-beta.sig

    >

    [

    ...]

    >

    Success! Bitcoin block 783050 attests existence as of 2023-03-29 CEST
  • Check that the date of the timestamp is close to the release date of the LND binary.

Installation

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

  • Install LND

    $

    tar

    -xzf

    lnd-linux-arm64-v0.16.0-beta.tar.gz

    $

    sudo install

    -m

    0755

    -o

    root

    -g

    root

    -t

    /usr/local/bin lnd-linux-arm64-v0.16.0-beta/

    *

    $

    lnd

    --version

    >

    lnd version v0.16.0-beta

    commit

    =

    v0.16.0-beta

Data directory

Now that LND is installed, we need to configure it to work with Bitcoin Core and run automatically on startup.

  • Create the “lnd” service user, and add it to the groups “bitcoin” and “debian-tor”

    $

    sudo

    adduser

    --disabled-password

    --gecos

    ""

    lnd

    $

    sudo

    usermod

    -a

    -G

    bitcoin,debian-tor lnd
  • Add the user “admin” to the group “lnd”

    $

    sudo

    adduser admin lnd
  • Create the LND data directory

    $

    sudo mkdir

    /data/lnd

    $

    sudo chown

    -R

    lnd:lnd /data/lnd
  • Open a “lnd” user session

    $

    sudo

    su - lnd
  • Create symbolic links pointing to the LND and bitcoin data directories

    $

    ln

    -s

    /data/lnd /home/lnd/.lnd

    $

    ln

    -s

    /data/bitcoin /home/lnd/.bitcoin
  • Display the links and check that they’re not shown in red (this would indicate an error)

    $

    ls

    -la

    symlinks

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 Raspberry Pi, 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, with the bonus guides linked below. To give some perspective: other Lightning implementations like c-lightning or Eclair don’t even have a password.

  • As user “lnd”, create a text file and enter your LND wallet password [C]. Save and exit.

    $

    nano /data/lnd/password.txt
  • Tighten access privileges and make the file readable only for user “lnd”:

    $

    chmod

    600 /data/lnd/password.txt

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

  • Example by LND: using a password manager with named pipe
  • More to come…

Configuration

  • Create the LND configuration file and paste the following content (adjust to your alias). Save and exit.

    $

    nano /data/lnd/lnd.conf

    # RaspiBolt: lnd configuration

    # /data/lnd/lnd.conf

    [

    Application Options]

    alias

    =

    YOUR_FANCY_ALIAS

    debuglevel

    =

    info

    maxpendingchannels

    =

    5

    listen

    =

    localhost

    # Password: automatically unlock wallet with the password in this file

    # -- comment out to manually unlock wallet, and see RaspiBolt guide for more secure options

    wallet-unlock-password-file

    =

    /data/lnd/password.txt wallet-unlock-allow-create

    =

    true

    # Automatically regenerate certificate when near expiration

    tlsautorefresh

    =

    true

    # Do not include the interface IPs or the system hostname in TLS certificate.

    tlsdisableautofill

    =

    true

    # Explicitly define any additional domain names for the certificate that will be created.

    # tlsextradomain=raspibolt.local

    # tlsextradomain=raspibolt.public.domainname.com

    # Channel settings

    bitcoin.basefee

    =

    1000 bitcoin.feerate

    =

    1

    minchansize

    =

    100000 accept-keysend

    =

    true

    accept-amp

    =

    true

    protocol.wumbo-channels

    =

    true

    coop-close-target-confs

    =

    24

    # Watchtower

    wtclient.active

    =

    true

    # Performance

    gc-canceled-invoices-on-startup

    =

    true

    gc-canceled-invoices-on-the-fly

    =

    true

    ignore-historical-gossip-filters

    =

    1 stagger-initial-reconnect

    =

    true

    # Database

    [

    bolt] db.bolt.auto-compact

    =

    true

    db.bolt.auto-compact-min-age

    =

    168h

    [

    Bitcoin] bitcoin.active

    =

    true

    bitcoin.mainnet

    =

    true

    bitcoin.node

    =

    bitcoind

    [

    tor] tor.active

    =

    true

    tor.v3

    =

    true

    tor.streamisolation

    =

    true

🔍 This is a standard configuration. Check the official LND sample-lnd.conf with all possible options, and visit the Lightning Node Management site by Openoms to learn more.

Run LND

Still with user “lnd”, we first start LND manually to check if everything works fine.

$

lnd
Attempting automatic RPC configuration to bitcoind
Automatically obtained bitcoind's RPC credentials
2021-11-13 08:16:34.985 [INF] LTND: Version: 0.16.0-beta commit=v0.16.0-beta, build=production, logging=default, debuglevel=info
2021-11-13 08:16:34.985 [INF] LTND: Active chain: Bitcoin (network=mainnet)
...
2021-11-13 08:16:35.028 [INF] LTND: Waiting for wallet encryption password. Use `lncli create` to create a wallet, `lncli unlock` to unlock an existing wallet, or `lncli changepassword` to change the password of an existing wallet and unlock it.

The daemon prints the status information directly to the command line. This means that we cannot use that session without stopping the server. We need to open a second SSH session.

Wallet setup

Start your SSH program (eg. PuTTY) a second time, connect to the Pi and log in as “admin”. Commands for the second session start with the prompt $2 (which must not be entered).

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

  • Start a “lnd” user session

    $2

    sudo

    su - lnd
  • Create the LND wallet

    $2

    lncli create
  • Enter your password [C] as wallet password (it must be exactly the same you stored in password.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.

    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. secret     2. secret    3. secret     4. secret
    ...
    

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 /data/lnd-backup/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 RaspiBolt backup card, or even stamp the seed words into metal. 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.

  • Exit the user “lnd” user session, and then exit the second SSH session altogether

    $2

    exit

    $2

    exit

  • Back in your first SSH session with user “lnd”, LND is still running. Stop LND with Ctrl-C.

  • Start LND agin and check if the wallet is unlocked automatically. On success, stop LND again.

    $

    lnd

    >

    ...

    >

    Started LND Lightning Network Daemon.

    >

    Attempting automatic RPC configuration to bitcoind

    >

    Automatically obtained bitcoinds RPC credentials

    >

    ...

    >

    LTND: Attempting automatic wallet unlock with password

    >

    LNWL: Opened wallet

    >

    ...

    # stop LND with `Ctrl-C`

Autostart on boot

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

  • Exit the second “lnd” user session back to “admin”

    $

    exit

  • Create LND systemd unit with the following content. Save and exit.

    $

    sudo

    nano /etc/systemd/system/lnd.service

    # RaspiBolt: systemd unit for lnd

    # /etc/systemd/system/lnd.service

    [

    Unit]

    Description

    =

    LND Lightning Network Daemon

    Wants

    =

    bitcoind.service

    After

    =

    bitcoind.service

    [

    Service]

    # Service execution

    ###################

    ExecStart

    =

    /usr/local/bin/lnd

    ExecStop

    =

    /usr/local/bin/lncli stop

    # Process management

    ####################

    Type

    =

    simple

    Restart

    =

    always

    RestartSec

    =

    30

    TimeoutSec

    =

    240

    LimitNOFILE

    =

    128000

    # Directory creation and permissions

    ####################################

    User

    =

    lnd

    # /run/lightningd

    RuntimeDirectory

    =

    lightningd

    RuntimeDirectoryMode

    =

    0710

    # Hardening measures

    ####################

    # Provide a private /tmp and /var/tmp.

    PrivateTmp

    =

    true

    # Mount /usr, /boot/ and /etc read-only for the process.

    ProtectSystem

    =

    full

    # Disallow the process and all of its children to gain

    # new privileges through execve().

    NoNewPrivileges

    =

    true

    # Use a new /dev namespace only populated with API pseudo devices

    # such as /dev/null, /dev/zero and /dev/random.

    PrivateDevices

    =

    true

    # Deny the creation of writable and executable memory mappings.

    MemoryDenyWriteExecute

    =

    true

    [

    Install]

    WantedBy

    =

    multi-user.target
  • Enable, start and unlock LND

    $

    sudo

    systemctl

    enable

    lnd

    $

    sudo

    systemctl start lnd

    $

    systemctl status lnd
  • Now, the daemon information is no longer displayed on the command line but written into the system journal. You can check on it using the following command (and exit with Ctrl-C).

    $

    sudo

    journalctl

    -f

    -u

    lnd

Allow user “admin” to work with LND

We interact with LND using the application lncli. At the moment, only the user “lnd” has the necessary access privileges. To make the user “admin” the main administrative user, we make sure it can interact with LND as well.

  • Newly added groups become active only in a new user session. Log out from SSH.

    $

    exit

  • Log in as user “admin” again.

  • Link the LND data directory in the user “admin” home. As a member or the group “lnd”, admin has read-only access to certain files. We also need to make all directories browsable for the group (with g+X) and allow it to read the file admin.macaroon

    $

    ln

    -s

    /data/lnd /home/admin/.lnd

    $

    sudo chmod

    -R

    g+X /data/lnd/data/

    $

    sudo chmod

    g+r /data/lnd/data/chain/bitcoin/mainnet/admin.macaroon
  • Check if you can use lncli by querying LND for information

    $

    lncli getinfo

LND in action

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 RaspiBolt, you have “skin in the game”.

Funding your Lightning node

  • Generate a new Bitcoin address (p2wkh = native SegWit/Bech32) to receive funds on-chain and send a small amount of Bitcoin to it from any wallet of your choice.

    newaddress

    $

    lncli newaddress p2wkh

    >

    "address"

    :

    "bc1..."

  • Check your LND wallet balance

    walletbalance

    $

    lncli walletbalance

    {

    "total_balance"

    :

    "712345"

    ,

    "confirmed_balance"

    :

    "0"

    ,

    "unconfirmed_balance"

    :

    "712345"

    }

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 or 1ML.com 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+.

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

    $

    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

    One Bitcoin equals 100 million satoshis, so at $10’000/BTC, $10 amount to 0.001 BTC or 100’000 satoshis. To avoid mistakes, you can just use an online converter.

    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)

    $

    lncli openchannel

    --sat_per_vbyte

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

    walletbalance or channelbalance

    $

    lncli walletbalance

    $

    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

    $

    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 a single satoshi! You simply need to input my node pukey Stadicus node, the amount in satoshis and add the –keysend flag.

      

    *

    lncli sendpayment

    --dest

    02acd93e3352fd59066ca3f23e8865de1926301e8be03c6a52f0f7e43533fe9888

    --amt

    1

    --keysend

Adding watchtowers

Lightning channels need to be monitored to prevent malicious behavior by your channel peers. If your RaspiBolt 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.

  • With user “LND”, add the Lightning Network+ watchtower as a first example

    $

    sudo

    su - lnd

    $

    lncli wtclient add 023bad37e5795654cecc69b43599da8bd5789ac633c098253f60494bde602b60bf@iiu4epqzm6cydqhezueenccjlyzrqeruntlzbx47mlmdgfwgtrll66qd.onion:9911
  • Check if the watchtower is active

    $

    lncli wtclient towers

    {

    "towers"

    :

    [

    {

    "pubkey"

    :

    "023bad37e5795654cecc69b43599da8bd5789ac633c098253f60494bde602b60bf"

    ,

    "addresses"

    :

    [

    "iiu4epqzm6cydqhezueenccjlyzrqeruntlzbx47mlmdgfwgtrll66qd.onion:9911"

    ]

    ,

    "active_session_candidate"

    :

    true

    ,

    "num_sessions"

    : 0,

    "sessions"

    :

    [

    ]

    }

    ,

    ]

    }

  • Check out this list of altruistic public watchtowers maintained by Openoms, and add a few more.

  • If you want to list your towers

    $

    lncli wtclient towers
  • If you want to deactivate an active tower

    $

    lncli wtclient remove <pubkey>

More 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]
  • Find out some general stats about your node: getinfo

    $

    lncli getinfo
  • Check the peers you are currently connected to: listpeers

    $

    lncli listpeers
  • Check the status of your pending channels: pendingchannels

    $

    lncli pendingchannels
  • Check the status of your active channels: listchannels

    $

    lncli listchannels
  • Before paying an invoice, you should decode it to check if the amount and other infos are correct: decodepayreq

    $

    lncli decodepayreq

    [

    INVOICE]
  • Pay an invoice:

    $

    lncli payinvoice

    [

    INVOICE]
  • Send a payment to a node without invoice using AMP (both sender and receiver nodes have to have AMP enabled): sendpayment

    $

    lncli sendpayment

    --amp

    --fee_limit

    1

    --dest

    =

    <node_pubkey>

    --final_cltv_delta

    =

    144

    --amt

    =

    <amount_in_sats>
  • Check the payments that you sent: listpayments

    $

    lncli listpayments
  • Create an invoice: addinvoice

    $

    lncli addinvoice

    [

    AMOUNT_IN_SATOSHIS]
  • List all invoices: listinvoices

    $

    lncli listinvoices
  • to close a channel, you need the following two arguments that can be determined with listchannels and are listed as “channelpoint”: FUNDING_TXID:OUTPUT_INDEX

    closechannel

    $

    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]

🔍 more: full LND API reference

For the future: upgrade LND

Upgrading LND can lead to a number of issues. Always read the LND release notes 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

  • As “admin” user, stop the LND service

    $

    sudo

    systemctl stop lnd
  • Download, verify and install the latest LND binaries as described in the LND section of this guide.

  • Restart the services with the new configuration

    $

    sudo

    systemctl restart lnd

Next: Channel backup »