The perfect Bitcoin ⚡️Lightning️⚡ node

My goal is to create the perfect Bitcoin Lightning node, running on unfairly cheap hardware, set up completely trustless from scratch. This guide brings you what I think is a near perfect solution.

Running our own nodes is important, both to learn more about this amazing technology, and also keep the network decentralized by not trusting any third party.

Being able to do that on cheap hardware is important. This is why I published the Beginner’s Guide to Lightning on a Raspberry Pi (my RaspiBolt) which created a lot of positive feedback.

The Pi is available everywhere and has a huge community. It has some drawbacks, however, mainly in the area of performance and the the hassle with attaching external storage that is important when storing the whole Bitcoin blockchain. One feedback sparked my interest to consider the Odroid HC2 mini-computer.

Compared to the Rasperry Pi, it has the following advantages:

  • price comparable to Raspberry Pi
  • more powerful (8 core CPU, 2 GB RAM, Gigabit Ethernet)
  • internal housing of harddisk, direct connection using SATA3
  • only one power adapter for everything

Not available are features like HDMI output, built-in Wifi or GPIO pins, all of which are not relevant to this project, however.

Performance is way better and production is guaranteed until at least 2020 (and longer, if parts are available), thus it seems more future-proof.

Performance of Odroid HC2 is identical to XU4 (which is more of a media pc)

I decided to try this setup, ordered the Odroid HC2 and, after setting it up and running it for a month now, I think this is as good as it gets for a low-cost Bitcoin / Lightning node. This is why I am going to describe the setup of a Bitcoin Core full node with a LND lightning node here, in case you might want to try this as well.

As this project needs a cheesy name as well, I’ll call my node Thundroid. :-)

Purpose

My aim is to set up a trustless Bitcoin Core and Lightning node that runs 24/7, is part of and supports the decentralization of the Lightning network by routing payments and can be used to send and receive personal payments using the command line interface.

This server is set up without graphical user interface and is used remotely using the Secure Shell (SSH) command line. In the future, this server should function as my personal backend for desktop and mobile wallets, I’m not quite there yet. So, command line it is for the moment.

Spoiler alert, this is the goal of this guide, simply buying a Blockaccino:

Things on my bucket & todo list: web interface, desktop und mobile wallet.

Please note that only the setup for Lightning on Bitcoin testnet is described at the moment. Once a well-tested mainnet release of Lightning is available, I will update this guide and provide update instructions.

Target audience

This will not be a beginner’s guide, but the setup is comparable to the RaspiBolt guide, so you can easily get more detailed explanations from there. The goal is to do everything ourselves, no shortcuts that involve trust in a 3rd party allowed. This makes this guide quite technical, but I try to make it as straightforward as possible.

Also, please be aware that all Lightning implementations are still in alpha phase and bugs are to be expected. If you like to learn about Linux, Bitcoin and Lightning, this guide is for you.

If you are curious about the inner workings of the Lightning Network, this article in Bitcoin Magazine (by the excellent Aaron van Wirdum) offers a very good introduction:

1) Hardware Requirements

I ordered the following items directly with Hardkernel in Singapore. There are resellers available worldwide, but not for Switzerland, unfortunately.

You also need the following:

  • Micro SD card: 8 GB or more, incl. adapter to your regular computer
  • Internal hard disk: 500 GB or more, SATA, 3.5" hdd, or 2.5" hdd/ssd
  • Network RJ45 cable

Considerations for Bitcoin mainnet

This guide is configured to use the Bitcoin testnet. You can easily download and verify the blockchain using the Odroid HC2. However, for a switch to Bitcoin mainnet, will will need to initially download and verify the Bitcoin blockchain on a regular computer (only once, not regularly).

There are multiple options (not covered in this guide) to transfer the mainnet blockchain (~200 GB) to your node:

  • connect your internal hard disk directly to your computer,
  • transfer the blockchain over the network
  • use an additional external hard disk to transfer the data via USB.

We are going to format the internal hard disk using the Ext4 file system, which is not compatible with windows. If you want to connect the hard disk later to a windows computer, you need additional software (free for once-time-usage).

Assemble the hardware

Assembly is easy, just enter the hard disk and fix it with the the screws included with your Odroid. If you ordered the plastic case, just slide it over the metal body.

2) Write down your passwords

Create strong passwords for the following users and interfaces. As this is not a Pi, I will use the user “admin” instead of “pi” in the original guide.

[ A ] User password for “admin” and “root”
[ C ] User password for “bitcoin”
[ D ] Bitcoin wallet password
[ E ] Bitcoin RPC password
[ F ] Lightning API password

3) Installing the operating system

We use Ubuntu 16.04.03 LTE (Minimal, Bare OS) that is provided by Hardkernel.

Exact file used: ubuntu-16.04.3-4.14-minimal-odroid-xu4-20171213.img.xz

Download the image, flash it on your MicroSD card, put it into your Thundroid, connect it to your network via cable and connect the power adapter. The initial boot can take several minutes. SSH is on by default.

4) Connecting to the network

Give your Thundroid a fixed IP address in the DCHP settings of your router. See section (4) of the RaspiBolt guide for additional information.

5) Working on your Thundroid

The command prompt

Everything is configured on the Linux command prompt. Throughout this guide I use the following notation:

# this is a comment, just for information
$ command            This is a command to enter (without the $) 
and confirm with the enter key
No prefix            This is either an output of the command above
or something you can copy/paste into a file

When you enter commands, you can use the “Tab” key for autocompletion, eg. for commands, directories or filenames.

If you are using Windows, I recommend to use the SSH client KiTTY. You can copy text from the shell by selecting it with your mouse (no need to click anything), and paste stuff with a right-click.

Basic configuration

Connect to the node using SSH. The access credentials are

  • user: root
  • password: odroid
# change root password to [password A]
$ passwd
# update the operating system
$ apt update
$ apt upgrade
$ apt dist-upgrade
$ apt install linux-image-xu3
# answer [y], then [no] (do not abort)
# install some additional software
$ apt install htop git curl bash-completion jq
# set time zone & localization
$ dpkg-reconfigure tzdata
$ dpkg-reconfigure locales

When using the Nano text editor, you can use the same keyboard shortcuts to save (Ctrl-O, confirm or change filename & press enter) and exit (Ctrl-X).

# change hostname (replace "odroid" with "thundroid" :)
$ nano /etc/hostname
$ nano /etc/hosts
# disable Swap file (this would degrade the MicroSD card)
$ swapoff --all
# create user "admin" and change password to [password A]
$ useradd -m admin
$ adduser admin sudo
$ passwd admin
# create user "bitcoin" and change password to [password C]
$ useradd -m bitcoin
$ passwd bitcoin

Mounting the hard disk

# get NAME for hard disk
$ lsblk -o UUID,NAME,FSTYPE,SIZE,LABEL,MODEL
# format hard disk (use [NAME] from above, e.g /dev/sda1)
$ mkfs.ext4 /dev/[NAME]
# get UUID for hard disk, copy into notepad
$ lsblk -o UUID,NAME,FSTYPE,SIZE,LABEL,MODEL
# edit fstab and enter new line (replace UUID) at the end 
$ nano /etc/fstab
UUID=123456 /mnt/hdd ext4 noexec,defaults 0 0
# create mount point, mount, check and set owner
$ mkdir /mnt/hdd
$ mount -a
$ df /mnt/hdd
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/sda1 961300808 600388836 312057600 66% /mnt/hdd
$ chown bitcoin:bitcoin /mnt/hdd/
# make sure user "admin" uses bash as standard shell
$ sudo chsh admin -s /bin/bash
# restart
$ shutdown -r now

6) Hardening your Thundroid

Your Thundroid will handle money and needs to be super-secure. Login as “admin” (we will not use “root” again).

fail2ban

Fail2ban monitors SSH login attempts and bans a remote peer after 5 unsuccessful tries for 10 minutes.

$ sudo apt install fail2ban

UFW: Uncomplicated Firewall

The firewall denies all connection attempts from other peers by default and allows only specific ports to be used.

The line ufw allow from 192.168.0.0/24 … below assumes that the IP address of your Pi is something like 192.168.0.???, the ??? being any number from 0 to 255. If your IP address is 12.34.56.78, you must adapt this line to ufw allow from 12.34.56.**0**/24 ….

# change session to "root"
$ sudo su
$ apt install ufw
$ ufw default deny incoming
$ ufw default allow outgoing
$ ufw allow from 192.168.0.0/24 to any port 22 comment 'allow SSH from local LAN'
$ ufw allow 9735 comment 'allow Lightning'
$ ufw deny 8333 comment 'deny Bitcoin mainnet'
$ ufw allow 18333 comment 'allow Bitcoin testnet'
$ ufw enable
$ systemctl enable ufw
$ ufw status
# exit "root" session back to "admin"
$ exit

SSH Keys

We will disable login via password completely and require a SSH certificate. To create it for your “admin” user, please follow this guide:

If you can log in as “admin” with your SSH key (check!), we disable the password login.

$ sudo nano /etc/ssh/sshd_config
# change ChallengeResponseAuthentication and PasswordAuthentication
# to "no" (uncomment if necessary), save and exit

# copy the SSH public key for user "root", just in case
$ sudo mkdir /root/.ssh
$ sudo cp /home/admin/.ssh/authorized_keys /root/.ssh/
$ sudo chown -R root:root /root/.ssh/
$ sudo chmod -R 700 /root/.ssh/
$ sudo systemctl restart sshd.service

You can now only login with “admin” or “root” and your SSH key. As you cannot connect a screen to the Odroid, SSH is your only option.

⚠️ Backup your SSH key! There is no fallback login!

Worst case scenario: you need to flash the MicroSD card and set up the system again, all important stuff is still on the harddrive.

Optional: add Tor connectivity

You can make your node accessible through the Tor network. Please check your configuration carefully and also check the original article of Damian Mee for additional details.

# add the following lines to the file sources.list
$ sudo nano /etc/apt/sources.list
deb http://deb.torproject.org/torproject.org xenial main
deb-src http://deb.torproject.org/torproject.org xenial main
# add the Tor signing key
$ gpg --keyserver keys.gnupg.net --recv 886DDD89
$ gpg --export A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89 | sudo apt-key add -
$  sudo apt install tor
# configure Tor
$ sudo nano /etc/tor/torrc
$ sudo service tor@default restart
$ sudo usermod -a -G debian-tor admin
$ sudo usermod -a -G debian-tor bitcoin

6½) Prettify your Thundroid

The following is not exactly necessary, but I think still worth the effort. 🖖

Bash completion

As user “admin”, install bash completion scripts for Bitcoin Core and all Lightning projects. You then can complete commands by pressing the Tab key (e.g. bitcoin-cli getblockch [Tab]bitcoin-cli getblockchaininfo )

$ mkdir /home/admin/download
$ cd /home/admin/download
$ wget https://raw.githubusercontent.com/bitcoin/bitcoin/master/contrib/bitcoin-cli.bash-completion
$ wget https://raw.githubusercontent.com/ACINQ/eclair/master/contrib/eclair-cli.bash-completion
$ wget https://raw.githubusercontent.com/lightningnetwork/lnd/master/contrib/lncli.bash-completion
$ wget https://raw.githubusercontent.com/ElementsProject/lightning/master/contrib/lightning-cli.bash-completion
$ sudo cp *.bash-completion /etc/bash_completion.d/

Pimp the command line prompt

You can prettify your command prompt for each user by enabling color output and setting a custom prompt. Use either the yellow or red user, not both.

I use the red prompt for user “admin”, and the yellow prompt for “bitcoin”.
# edit .bashrc with user "admin", use options from below.

$ nano /home/admin/.bashrc
$ sudo nano /home/bitcoin/.bashrc
# reload .bashrc (or just wait until next login)
$ source /home/admin/.bashrc
It’s safest to comment # the original line and add the new color prompt below

7) Bitcoin Core

Installing the Bitcoin Core software connects our node to the Bitcoin network, and allows us to use it also as a trustless Full Node.

Install it the speedy way with the following run-through, or check the RaspiBolt guide chapter (7) for more details (just use the user “admin” instead of “pi”).

$ mkdir /home/admin/download
$ cd /home/admin/download
# download the latest Bitcoin Core ARM binaries (check https://bitcoin.org/en/download)
$ wget https://bitcoin.org/bin/bitcoin-core-0.16.0/bitcoin-0.16.0-arm-linux-gnueabihf.tar.gz
$ wget https://bitcoin.org/bin/bitcoin-core-0.16.0/SHA256SUMS.asc
$ wget https://bitcoin.org/laanwj-releases.asc
# check that the reference checksum matches the real checksum
# output: "bitcoin-0.16.0-arm-linux-gnueabihf.tar.gz: OK"
$ sha256sum --check SHA256SUMS.asc --ignore-missing
# manually check the fingerprint of the public key:
# 01EA 5486 DE18 A882 D4C2 6845 90C8 019E 36C2 E964
$ gpg --with-fingerprint ./laanwj-releases.asc
# import the public key and verify the signed checksum file
# output: "Good signature from Wladimir ..."
# check the fingerprint again in case of malicious keys
# 01EA 5486 DE18 A882 D4C2 6845 90C8 019E 36C2 E964
$ gpg --import ./laanwj-releases.asc
$ gpg --verify SHA256SUMS.asc
# extract, install and check active version
$ tar -xvf bitcoin-0.16.0-arm-linux-gnueabihf.tar.gz
$ sudo install -m 0755 -o root -g root -t /usr/local/bin bitcoin-0.16.0/bin/*
$ bitcoind --version
Bitcoin Core Daemon version v0.16.0
# configuration
$ sudo su bitcoin
$ mkdir /mnt/hdd/bitcoin_testnet
$ ln -s /mnt/hdd/bitcoin_testnet /home/bitcoin/.bitcoin
# copy/paste the following Bitcoin configuration file 
# (replace PASSWORD_[E] with your password)
$ nano /home/bitcoin/.bitcoin/bitcoin.conf
# back to user "admin"
$ exit
# copy/paste the following systemd unit file
$ sudo nano /etc/systemd/system/bitcoind.service
# Enable the unit file and start it manually
$ sudo systemctl enable bitcoind
$ sudo systemctl start bitcoind
$ systemctl status bitcoind
# check bitcoind operations (exit with Ctrl-C)
$ sudo tail -f /home/bitcoin/.bitcoin/testnet3/debug.log
# check bitcoin blockchain verification progress
$ sudo su bitcoin
$ bitcoin-cli getblockchaininfo
# check public visibility
$ curl -sL https://bitnodes.earn.com/api/v1/nodes/me-18333/ | jq
{
"success": true
}

Optional: if you installed Tor, check if it is recognized by bitcoind.

$ sudo cat /home/bitcoin/.bitcoin/testnet3/debug.log | grep tor:
2018-02-25 13:50:23 tor: Thread interrupt
2018-02-25 13:51:19 tor: Got service ID tysrolynofiqkigu, advertising service tysrolynofiqkigu.onion:28333

At this time, no wallet needs to be created, as LND does not require the Bitcoin Core wallet.

8) Lightning Network

We will compile and install the LND Lightning node. Other Lighting nodes like Eclair or c-lightning can also be installed, even simultaneously. However, only one should run at any given time.

Public IP script

To announce our public IP address to the Lightning network, we need to get it first from a source outside our network. As user “admin”, create the following script that checks the IP all 5 minutes and stores it locally.

# create script getpublicip.sh (paste the following gist)
$ sudo nano /usr/local/bin/getpublicip.sh
# make it executable
$ sudo chmod +x /usr/local/bin/getpublicip.sh
# paste systemd unit [getpublicip.service], save and exit
$ sudo nano /etc/systemd/system/getpublicip.service
# enable systemd startup 
$ sudo systemctl enable getpublicip
$ sudo systemctl start getpublicip
$ sudo systemctl status getpublicip
# check if data file has been created
$ cat /run/publicip
PUBLICIP=91.190.22.151

Lightning: LND

As user “admin”, we first need to install Go compiler, clone the LND source code, compile and install it.

LND installation

⚠️ The following code box is outdated. Until I fix it, please follow the official instructions in the LND repository.

# install Go
# (if you have an older version of Go installed, make sure
# to delete it first: $ sudo rm -rf /usr/local/go/ )
$ cd /home/admin/download
$ wget https://dl.google.com/go/go1.10.linux-armv6l.tar.gz
$ sudo tar -C /usr/local -xzf go1.10.linux-armv6l.tar.gz
# insert both export statements at the end of .bashrc
$ nano /home/admin/.bashrc
export GOPATH=/home/admin/go
export PATH=$PATH:/usr/local/go/bin:$GOPATH/bin
$ source /home/admin/.bashrc
$ go get -u github.com/Masterminds/glide
# the next command is one line
$ git clone https://github.com/lightningnetwork/lnd $GOPATH/src/github.com/lightningnetwork/lnd
# build and install
$ cd $GOPATH/src/github.com/lightningnetwork/lnd
$ git pull && glide install
$ go install . ./cmd/...
$ cd $GOPATH/bin
$ sudo cp lnd lncli /usr/local/bin/

LND configuration

# session with user "bitcoin"
$ sudo su bitcoin
$ mkdir /mnt/hdd/lnd_testnet
$ ln -s /mnt/hdd/lnd_testnet /home/bitcoin/.lnd
# create LND configuration and paste the following content
# (change the alias to your battle name)
$ nano /home/bitcoin/.lnd/lnd.conf
# exit user session back to user "admin"
$ exit
# create LND systemd unit and paste the following content
$ sudo nano /etc/systemd/system/lnd.service
# enable and start LND
$ sudo systemctl enable lnd
$ sudo systemctl start lnd
$ systemctl status lnd
● lnd.service - LND Lightning Daemon
Loaded: loaded (/etc/systemd/system/lnd.service; ........)
Active: active (running) ......
Main PID: 9064 (lnd)
CGroup: /system.slice/lnd.service
└─9064 /usr/local/bin/lnd --externalip=91.190.22.151
# follow the LND logfile (exit with Ctrl-C)
$ sudo journalctl -f -u lnd

LND wallet setup

LND logfile: Waiting for wallet encryption password. Use `lncli create` to create wallet, or `lncli unlock` to unlock already created wallet.

For security reasons, only user “bitcoin” can interact with bitcoind and lnd.

$ sudo su bitcoin
# first time starting LND
$ lncli create
# after that, every time starting LND
$ lncli unlock
# get some information & a bitcoin deposit address 
$ lncli newaddress np2wkh
{
"address": "2N5TDE1KBP3k8oQ9i3yCRahUJ7y6bUHNeid"
}

Now, get some tBTC from a Faucet, eg. https://testnet.coinfaucet.eu and send them to this new address. As we enabled the “autopilot” feature, LND will automatically open channels.

Get yourself a payment request on StarBlocks or Y’alls and move some coins!

$ lncli walletbalance
$ lncli listchannels
$ lncli sendpayment --pay_req=lntb32u1pdg7p...y0gtw6qtq0gcpk50kww

👉 additional information: LND API reference with examples

Web interface

Next up on my bucket list is a nice, clean web interface, like this one:

Check back for further details soon! 😏

Various

Add system overview

To get a quick overview over the system status, I created a shell script that is shown on login or on demand.

This script will run as root, so please check it before blindly trusting me.

# as user "admin"
$ cd /home/admin/download/
$ wget https://gist.githubusercontent.com/Stadicus/ffbbd855d23cd068f7b739cae6440f4b/raw/ab2c97bd554c003b88f5e9a8793a047805d5e4b0/20-thundroid-welcome
# check script & exit
$ nano 20-thundroid-welcome
# delete existing welcome scripts and install
$ sudo rm /etc/update-motd.d/*
$ sudo cp 20-thundroid-welcome /etc/update-motd.d/
$ sudo chmod +x /etc/update-motd.d/20-thundroid-welcome
$ sudo ln -s /etc/update-motd.d/20-thundroid-welcome /usr/local/bin/thundroid

You can now start the script with thundroid and it is shown every time you log in to your Thundroid.

Add safe shutdown script

The Odroid HC2 manufacturer recommends to apply the following script to park the hard drive for a safe shutdown.

# as user "admin"
$ cd /home/admin/download
# download and check the script (it should look like listed below)
$ wget https://dn.odroid.com/5422/script/odroid.shutdown
$ cat odroid.shutdown
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#!/bin/bash
exec </dev/null </dev/null 2>/dev/null
export LANG=C LC_ALL=C
# In all cases, we want the media to be in quiescent, clean state.
sync
[ -x /sbin/mdadm ] && /sbin/mdadm --wait-clean --scan
# Function used to park all SATA disks.
function ParkDisks() {
if [ -x /sbin/hdparm ]; then
Wait=0
for Dev in /sys/block/sd* ; do
[ -e $Dev ] && /sbin/hdparm -y /dev/${Dev##*/} && Wait=2
sleep $Wait
echo 1 > /sys/class/block/${Dev##*/}/device/delete
done
sleep $Wait
fi
}
case "$1" in
# reboot|kexec)
# Do not park disks when rebooting or switching kernels.
# ;;
*)
ParkDisks
;;
esac
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
$ sudo install -o root -g root -m 0755 ./odroid.shutdown /lib/systemd/system-shutdown/odroid.shutdown

Closing comments

Let me know what you think. And if you actually did set up a node with this guide, I’d love to hear from you!

Please post questions, suggestions and adulation in this Reddit post.