Bitcoin Lightning Network: run your node at home for fun and (no) profit ⚡🤑

Simonluca Landi
Coinmonks
Published in
11 min readSep 23, 2018

--

Did you ever consider to install a Bitcoin Lightning Network on your own hardware? Here is how I did it.

The mandatory introduction: What is Lightning Network?

Quoting from lightning.network

Scalable, Instant Bitcoin/Blockchain Transactions

Lightning Network is trying to solve a few of the current problems that the Bitcoin is suffering: scalability (number of transactions per second, right now Bitcoin handles about seven transactions per second), speed (time required to confirm a transaction,), cost (fee to pay for each transaction).

Curiously enough, it turns out that transaction speed and cost are some of the same problems that Bitcoin is solving, but “how long a minute depends on which side of the bathroom door you’re on”.

How it works?

The basic idea is to create a “channel” between 2 participants (let’s s Alice and Bob) and place some funds in, for example 1 BTC each. This requires a transaction on the Bitcoin blockchain, that locks the funds in a 2 multisig address. Here is the situation of the open channel:

Alice <= 1 BTC == 1 BTC => Bob # opening of a new channel

Alice and Bob can now make as many transactions as they want “off-chain”, they just need to exchange an updated “balance-sheet”: that is transaction signed by the other party (so it’s missing one of the signatures) that is not propagated to the bitcoin mainnet (yet), and that pays the updated funds to the participants.

Because the transaction are stored “off-chain”, Alice and Bob don’t need to wait for a confirmation from the main blockchain (instant transaction) nor to pay a fee to the mainnet miners (low fees).

Here is the situation of the open channel while the transactions are happening:

Alice <= 0.8 BTC == 1.2 BTC => Bob # Bob   sent 0.2 BTC to Alice
Alice <= 0.9 BTC == 1.1 BTC => Bob # Alice sent 0.1 BTC to Bob
Alice <= 0.4 BTC == 1.6 BTC => Bob # Alice sent 0.5 BTC to Bob

At any moment, a participant can make the balance-sheet “real”, signing the latest multisig transaction received from (and signed by) the other party, and broadcasting it a as regular Bitcoin transaction: the channel is now closed.

But the real power of the Lightning Network is the “six degrees of separation” idea: a participant can reach any other in just a few hops, if the “routing” is know. So if Alice has an open channel with Bob, and Bob has an open channel with Charlie, Alice can make a transaction with Charlie, and send for example 0.5 BTC to him. Here is the situation of the channels before transaction:

Alice <= 1 BTC == 1 BTC => Bob     # Bob has 3 BTC on 2 channels
Bob <= 2 BTC == 2 BTC => Charlie

and after the transaction:

Alice <= 0.5 BTC == 1.5 BTC => Bob     # Bob is still having 3 BTC
Bob <= 1.5 BTC == 2.5 BTC => Charlie

The total balance of Bob remains unchanged, while the balance of Charlie increase of 0.5 BTC and the balance of Alice decreased of 0.5 BTC.

If you don’t wanna hassle but still want to support the Bitcoin network, check Top Bitcoin and Lightning Node Providers.

Choosing the Lightning Network implementation to use

At the moment, I found only 2 main solutions that fully conforms to the Lightning Network specification (BOLTs) on the bitcoin mainnet:

I spent some time reading the features implemented by the each project, and then decide to install LND because:

  • Both projects require a full bitcoind backend daemon running on the same host (or an host reachable by net), but LND supports also a bitcoind node in prune mode: this means that you don’t need to store the full bitcoin blockchain locally (more than 200 GB at the moment), so the disk space requirements are less and I my spare 128 GB USB pen drive is more than enough.
  • LND has an experimental support for the neutrino backend (only testnet at the moment), a Bitcoin light client, that further reduce the disk space requirements: I bet that soon or later it will reach a production ready quality, and that I could switch to it.
  • LND has an automatic channel management (autopilot), that creates and manage channels.
  • LND has “nat” feature: this allow to advertise the node to the network as long as it’s behind a single NAT, automatically handling the change of the public IP address, so that less scripting tricks are required on the node.

Let’s the fun begin: Preparing the Lightning Network node

Before the installation, I read and digested a few good guides, that I have to give credits:

Hardware setup

I had a Raspberry Pi 3 Model B+ mostly unused at home, a spare 128GB USB pen drive and a good network connectivity, so I tried to reuse those components. Please mind that you need to run your node 24/7, so a noiseless host is preferable. I installed some heat sinks on the Pi to avoid to have a fan running all the time while keeping the CPU temperature in a safe range. During the full installation process and further daily running operations, the temperature of the CPU never exceeded 65°C, safely below the max temperature of 85°C: anything above this and the Pi will throttle the CPU frequency to bring the temperature back down.

Setup of operating system on the Raspberry Pi

The installation of the operating system on the Pi is pretty simple: I decided to run an headless host, so I downloaded the Raspbian lite image from the official repository here: https://downloads.raspberrypi.org/raspbian_lite_latest and wrote the image to a MicroSD card, as explained here: https://www.raspberrypi.org/documentation/installation/installing-images/

Remember to add an empty file ssh in the root of the MicroSD: this is required to activate the ssh remote connectivity, otherwise you can’t login into the Pi from the network and configure it.

Then using the nmap map tool I found the ip address given by my home router to the Pi

> $ nmap -sP 192.168.1.0/24

and started an ssh session using the default user pi and password raspberry.

> $ ssh pi@192.168.1.134

To complete the configuration of the Pi, I run the raspi-config tool from the command line, tweaking the parameters to configure the WiFi SSID and pre-shared key, the locale, the timezone, enabling the ssh connectivity. Because the node is running headless, I also decreased the amount of memory made available to the GPU, in Memory split.

I then read the section “Mounting an HDD” in the raspberry guide https://www.raspberrypi.org/documentation/configuration/external-storage.md to mount my USD pen drive as a

Hardening

Because this is node is connected to the internet and is managing “money”, is better to enter the “paranoia” mode.

In order, what I did:

  • Removed the default user pi and created a new user to use as the main user to connect to the Pi.
  • Configured the passwordless ssh, following this guide https://www.raspberrypi.org/documentation/remote-access/ssh/passwordless.md and then allowed only the login via SSH certificate, changing the settings ChallengeResponseAuthentication and PasswordAuthentication to no in the file /etc/ssh/sshd_config
  • Created a “service” user bitcoin to run the demons required to interact with the Lightning Network, running sudo adduser bitcoin

What I should also do:

  • Install fail2ban to automatically ban IP addresses that make too many password failures. My Pi is behind an home router that doesn’t forward ssh port, so this is not strictly required, but this can prevent attacks generated from other compromised device in my home network (smartphones, laptops, smart tv….)
  • Install a firewall

Utilities

Here some handy aliases that I added to ~/.bashrc to make life easier:

# handy aliases
alias ..=’cd ..’
alias …=’cd ../..’
alias ….=’cd ../../..’
alias …..=’cd ../../../..’
alias ……=’cd ../../../../..’
alias 1=’cd -’
alias _=sudo
alias afind=’ack -il’
alias d=’dirs -v | head -10'
alias l=’ls -lah’
alias la=’ls -lAh’
alias ll=’ls -lh’
alias md=’mkdir -p’
alias rd=rmdir

Remember to reaload the ~/.bashrc

source ~/.bashrc

Then I installed iftop, a useful utility to monitor the network traffic that run in a terminal

sudo apt install iftopsudo iftop -i wlan0

PuTTY configuration

I use PuTTY to connect to the RaspberryPi. Here is how I changed the default configuration for the connection:

  • terminal → keyboard → The function keys and keypad → Linux
  • connection → seconds between keepalive → 30

Installing bitcoin core on Raspberry Pi

LND works with bitcoind as backend, so I first installed the latest bitcoin core software (https://bitcoincore.org). I quickly prepared a script, that I can reuse in my next projects, to download bitcoin core, verify the checksum and install the binaries. Just change the BITCOIND_VERSION to the latest one, that you can get from https://bitcoin.org/en/version-history

BITCOIND_VERSION=0.17.1
ARCH=arm-linux-gnueabihf
BITCOIND_ARCHIVE=bitcoin-${BITCOIND_VERSION}-${ARCH}.tar.gz
cd /tmp \
&& wget https://bitcoincore.org/bin/bitcoin-core-${BITCOIND_VERSION}/${BITCOIND_ARCHIVE} \
&& wget https://bitcoincore.org/bin/bitcoin-core-${BITCOIND_VERSION}/SHA256SUMS.asc \
&& wget https://bitcoin.org/laanwj-releases.asc \
&& SHA256=`grep "${BITCOIND_ARCHIVE}" SHA256SUMS.asc | awk '{print $1}'` \
&& echo $SHA256 \
&& echo "$SHA256 ${BITCOIND_ARCHIVE}" | sha256sum -c - \
&& gpg --import ./laanwj-releases.asc \
&& gpg --verify SHA256SUMS.asc \
&& tar -xzf ${BITCOIND_ARCHIVE} \
&& sudo install -m 0755 -o root -g root -t /usr/local/bin bitcoin-${BITCOIND_VERSION}/bin/* \
&& rm -rf /tmp/* \
&& bitcoind --version

As suggested by the Stadicus’s guide, I linked the bitcoind data directory /home/bitcoin/.bitcoin to a directory on the external USB pendrive:

sudo su bitcoin
ln -s /mnt/hdd/bitcoin /home/bitcoin/.bitcoin

and created the /home/bitcoin/.bitcoin/bitcoin.conf , activating the prune mode (note the prune=550 parameter).

vim /home/bitcoin/.bitcoin/bitcoin.conf# Bitcoind options
server=1
daemon=1
txindex=0
disablewallet=1
prune=550
# Connection settings
rpcuser=XXXXX
rpcpassword=YYYYY
onlynet=ipv4
zmqpubrawblock=tcp://127.0.0.1:29000
zmqpubrawtx=tcp://127.0.0.1:29001
# Raspberry Pi optimizations
dbcache=100
maxorphantx=10
maxmempool=50
maxconnections=40
maxuploadtarget=5000

Now the time consuming task, the Initial Block Download , the system need to download and verify all the blocks (from 2009) to catch up to the tip of the current best block chain.

I said before that lnd supports a bitcoin core backend in prune mode, this means that the old blocks are purged from the local disk, but they as still downloaded and validated: I started the process, but I quickly realized that full process would have taken more than 10 days… Someone suggests to use a separated device, with a more powerful CPU, to download and validate the blocks, and then copy the validated blocks on the Pi.

Spawning an Amazon EC2 instance to speedup the IBD

I don’t have a spare device fast enough to leave 24/7 crunching data, so I quickly spawn an m4.large instance on Amazon EC2 with an additional 50 GB EBS volume, visible as /dev/sdf . I used the amzn-ami-2018.03.a-amazon-ecs-optimized (ami-c91624b0), that natively supports docker, and then run bitcoind using a docker image. Here a quick recap:

  • Formatting and mount the additional EBS volume
sudo mkfs.ext4 /dev/sdf
sudo mkdir /mnt/bitcoin-data
sudo mount /dev/sdf /mnt/bitcoin-data/
  • Cloning repository and build the docker image
sudo yum install git
git clone https://github.com/dougvk/lightning-node.git
cd lightning-node
docker build . -t bitcoind
  • Running the bitcoind node for the first time checking the progress
docker run --name bitcoind_mainnet -d -v /mnt/bitcoin-data/bitcoind:/data -p 8333:8333 bitcoinddocker logs --tail 10 --follow bitcoind_mainnet
  • Stopping the bitcoind_mainnet process after a couple of minutes, and adding prune=550 to config to activate the blocks pruning
docker stop bitcoind_mainnet
docker rm bitcoind_mainnet
sudo yum install vim
sudo vim /mnt/bitcoin-data/bitcoind/bitcoin.conf
  • Starting again the bitcoind_mainnet process and wait for the Initial Block Download
docker run --name bitcoind_mainnet -d -v /mnt/bitcoin-data/bitcoind:/data -p 8333:8333 bitcoinddocker logs --tail 10 --follow bitcoind_mainnet

Then I waited for about 15 hours to eventually have progress=1.000000

2018–09–19 18:57:35 UpdateTip: new best=00000000000000000020aaf1f04a1ae2025408d044d936100a65c4b6e22a0853 height=542137 version=0x20000000 log2_work=89.696461 tx=342737382 date=’2018–09–19 18:57:22' progress=1.000000 cache=83.4MiB(410453txo) warning=’6 of last 100 blocks have unexpected version’

The cost of the m4.large instance is $0.10/hour, so it’s an affordable investment.

Now is possible to stop the bitcoind_mainnet process, and transfer the content of

  • /mnt/bitcoin-data/bitcoind/blocks
  • /mnt/bitcoin-data/bitcoind/chainstate
  • /mnt/bitcoin-data/bitcoind/database

to the RaspberryPi in /home/bitcoin/.bitcoin and terminate the EC2 instance (remember to delete the additional EBS volume too).

Starting bitcoind

Finally I created a systemd service, using this config (“stolen” from the Spadicus’s guide)

sudo vim /etc/systemd/system/bitcoind.service

[Unit]
Description=Bitcoin daemon
Wants=getpublicip.service
After=getpublicip.service

[Service]
ExecStartPre=/bin/sh -c 'sleep 30'
ExecStart=/usr/local/bin/bitcoind -daemon -conf=/home/bitcoin/.bitcoin/bitcoin.conf -pid=/home/bitcoin/.bitcoin/bitcoind.pid
PIDFile=/home/bitcoin/.bitcoin/bitcoind.pid
User=bitcoin
Group=bitcoin
Type=forking
KillMode=process
Restart=always
TimeoutSec=120
RestartSec=30

[Install]
WantedBy=multi-user.target

and finally started the service

sudo systemctl enable bitcoind.service
sudo systemctl start bitcoind.service
sudo systemctl status bitcoind.service
● bitcoind.service - Bitcoin daemon
Loaded: loaded (/etc/systemd/system/bitcoind.service; enabled; vendor preset: enabled)
Active: active (running) since Sun 2018-09-19 10:25:05 CEST; 6min ago
Process: 9482 ExecStart=/usr/local/bin/bitcoind -daemon -conf=/home/bitcoin/.bitcoin/bitcoin.conf -pid=/home/bitcoin/.bitcoin/bitcoind.pid (code=exited, sta
Process: 9353 ExecStartPre=/bin/sh -c sleep 30 (code=exited, status=0/SUCCESS)
Main PID: 9485 (bitcoind)
CGroup: /system.slice/bitcoind.service
└─9485 /usr/local/bin/bitcoind -daemon -conf=/home/bitcoin/.bitcoin/bitcoin.conf -pid=/home/bitcoin/.bitcoin/bitcoind.pid

Installing LND on Raspberry Pi

Finally, I installed LND (https://github.com/lightningnetwork/lnd/blob/master/README.md)

I first prepared a small script, (that I want to reuse for another project: running a Lightning Network node on Docker), to run as the “admin” user:

LND_VERSION=v0.5-beta
ARCH=linux-armv7
LND_ARCHIVE=lnd-${ARCH}-${LND_VERSION}.tar.gz
cd /tmp \
&& wget -q https://github.com/lightningnetwork/lnd/releases/download/${LND_VERSION}/${LND_ARCHIVE} \
&& wget -q https://github.com/lightningnetwork/lnd/releases/download/${LND_VERSION}/manifest-${LND_VERSION}.txt \
&& wget -q https://github.com/lightningnetwork/lnd/releases/download/${LND_VERSION}/manifest-${LND_VERSION}.txt.sig \
&& wget -q https://keybase.io/roasbeef/pgp_keys.asc \
&& SHA256=`grep "${LND_ARCHIVE}" manifest-${LND_VERSION}.txt | awk '{print $1}'` \
&& echo $SHA256 \
&& sha256sum ${LND_ARCHIVE} \
&& echo "$SHA256 ${LND_ARCHIVE}" | sha256sum -c - \
&& gpg --import ./pgp_keys.asc \
&& gpg --verify manifest-${LND_VERSION}.txt.sig \
&& tar -xzf ${LND_ARCHIVE} \
&& sudo install -m 0755 -o root -g root -t /usr/local/bin lnd-${ARCH}-${LND_VERSION}/* \
&& rm -rf /tmp/* \
&& lnd --version

Now the systemd service startup script, inspired from the Stadicus’s guide:

sudo vim /etc/systemd/system/lnd.service

[Unit]
Description=LND Lightning Daemon
Wants=bitcoind.service
After=bitcoind.service
[Service]
ExecStart=/usr/local/bin/lnd
PIDFile=/home/bitcoin/.lnd/lnd.pid
User=bitcoin
Group=bitcoin
LimitNOFILE=128000
Type=simple
KillMode=process
TimeoutSec=180
Restart=always
RestartSec=60
[Install]
WantedBy=multi-user.target

Because I’m running lnd with nat option, there is no need to explicitly set the --externalip=x.x.x.x startup parameter.

After the lnd daemon was running, I created a new wallet address to use:

lncli create

and then generated a new Bitcoin address to receive funds on-chain:

lncli newaddress np2wkh

and sent some BTC to it.

After a while, lnd started to create new channels on the Lightning Network:

lncli listchannels{
"channels": [
{
"active": true,
"remote_pubkey": "02ad6fb8d693dc1e4569bcedefadf5f72a931ae027dc0f0c544b34c1c6f3b9a02b",
"channel_point": "a33efe63ce594592cd6f8a3a6e8a68998f0dfa6b02b4e599df8b2f80f5d17f49:1",
"chan_id": "596552128324173825",
.....
.....
},
{
"active": true,
"remote_pubkey": "02fcc4b0a87749b022f05568d5ef893b7893bcbaa99276bbbb55d9e9abc1bdced0",
"channel_point": "878461d5557ba4b72951a1d0d02439ff8ed076a25a585b8b75e3666778e53895:0",
"chan_id": "596558725467734016",
.....
.....
}
]
}
The Raspberry Pi3 running a Lightning Network node

Let’s the (no) Profit begin

I’m running my Lightning Network node for only few days, but it’s clear that, as expected, is not generating any profit at all:

lncli feereport{
"channel_fees": [



],
"day_fee_sum": "0",
"week_fee_sum": "0",
"month_fee_sum": "0"
}

Why? ok, Lightning Network is still is it’s infancy, but IMHO the root cause is the topology of the network itself: it’s not distributed at all.

Let me explain this better: according to https://1ml.com/ today we have 3,544 nodes, providing a network capacity of 112.16 BTC in 12,160 channels, but guess what: the top 20 nodes by BTC capacity, just the 0.56% of them, control 85% of the total BTC capacity and 33% of total channels.

the “not decentralized” distribution of lightning network capacity

So it’s easy to figure out that the actual topology is a based on few “super hubs” that route the big part of the payments, and get the fees.

Is this a bad thing®? I’ll expose my opinion in a upcoming story.

Update 2019–04–03: Looking for a nice GUI to use on top of LND? Here is how to improve this setup: Installing RTL⚡ on Raspberry Pi🍓

Feel free to connect my Lightning Network node:
>$ lncli connect
039401f72bc0d40efb58b01de15527a2a5ae1943d7c29067b725a1467a93c7e66f@2.238.144.76:9735

--

--

Simonluca Landi
Coinmonks

80% of problems can be solved by marketing. I’m actually doing the things to solve the other 20%