In this workshop, we are going to setup a shared, private lightning network. Each of you will have your own Lightning Network node with which you will participate in the network. You will:

  • Create and fund your own bitcoin wallet
  • Connect to and open a channel with "Bob" (see the diagram below)
  • Send a payment to one of the other participants
  • Receive a payment

Here is a diagram to illustrate:

   LND                        LND                        LND
+ ----- +                   + --- +                   + ----- +
| Alice | <--- channel ---> | Bob | <--- channel ---> | Carol |
+ ----- +                   + --- +                   + ----- +
    |                          |                          |
    |                          |                          |
    +- - - - - - - - - - - - - +- - - - - - - - - - - - - +
                         + ----------- +
                         | BTC network | <--- BTCD
                         + ----------- +

This setup will allow each of the connected parties to send and receive payments with all the others - even if they are not directly connected by a channel. For example, Alice can send payments to Bob because they have a channel between each other but she can also send payments to Carol via Bob's node because he has a channel with Carol.

What is the Lightning Network?

First let's start with a brief, high-level explanation of what the Lightning Network is and how it works. From the community documentation:

The Lightning Network scales blockchains and enables trustless instant payments by keeping most transactions off-chain and leveraging the security of the underlying blockchain as an arbitration layer.

This is accomplished primarily through "payment channels", wherein two parties commit funds and pay each other by updating the balance redeemable by either party in the channel. This process is instant and saves users from having to wait for block confirmations before they can render goods or services.

And the key word to remember is network. Payment channels by themselves are not so interesting. But when a transaction can be passed along from one peer to the next until it reaches its recipient - without the need for a direct connection between the payer and payee - now that is interesting.

Our Own Private Bitcoin Network

Lightning Network nodes need to communicate with a bitcoin node to be able to create on-chain transactions, watch the blockchain for updates, and to open/close channels.

Your LN node will communicate with a remote bitcoin node that we are running specifically for this workshop. This bitcoin node will be running in "simnet" mode, which gives us full control over the blockchain. This allows us to instantly mine blocks so that we don't have to wait 10 minutes for each block - the average time per block on the mainnet or testnet networks.

Sharing Information During the Workshop

To make it easy for us to share information with each other during this workshop, we will use an open IRC channel:

  • In your browser go to
  • In the "Channels" field enter #ln-workshop
  • You can send private messages or just post in the general chat

Install and Configure Your Lightning Network Node

Download and install GoLang:

tar -xvf go1.10.4.linux-amd64.tar.gz
mv go ~/.go

Add GoLang-related environment variables to your user's .profile:

cat >> ~/.profile << EOL
export GOROOT="\$HOME/.go"
export GOPATH="\$GOROOT/packages"

Reload your user's .profile to load the new environment variables into the current terminal session:

source ~/.profile

Test GoLang setup:

go version

The previous command should output something like this:

go version go1.10.4.linux/amd64

Install lnd (Lightning Network Daemon):

go get -v -d
cd $GOPATH/src/
make && make install

Create an lnd configuration file:

mkdir -p ~/.lnd;
cat > ~/.lnd/lnd.conf << EOL

Create aliases for lnd and lncli:

cat >> ~/.profile << EOL
alias lnd-ws="lnd --lnddir=$HOME/.lnd"
alias lncli-ws="lncli --lnddir=$HOME/.lnd --network=simnet"

Make the new aliases usable in the current terminal window:

source ~/.profile

One last step to be able to communicate with the btcd node via a TLS-encrypted connection. Save the remote btcd node's TLS certificate to a file locally:

mkdir -p ~/.btcd;
cat > ~/.btcd/rpc.cert << EOL

Test your lnd setup:

lnd-ws --version

You should see something like this:

lnd version 0.5.0-beta commit=7fe095c54128b502e20b3482ef73f1b6ba737850

Great! Now let's move on to the next step.

Create and Fund Your Wallet

Start lnd:


You should see something like this:

2018-10-02 22:03:15.710 [INF] LTND: Version 0.5.0-beta commit=7fe095c54128b502e20b3482ef73f1b6ba737850
2018-10-02 22:03:15.710 [INF] LTND: Active chain: Bitcoin (network=simnet)
2018-10-02 22:03:15.714 [INF] CHDB: Checking for schema update: latest_version=6, db_version=6
2018-10-02 22:03:15.714 [INF] RPCS: Generating TLS certificates...
2018-10-02 22:03:15.731 [INF] RPCS: Done generating TLS certificates
2018-10-02 22:03:15.731 [INF] RPCS: password RPC server listening on
2018-10-02 22:03:15.732 [INF] RPCS: password gRPC proxy started at
2018-10-02 22:03:15.732 [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.

Leave lnd running.

In a new terminal window, run the following command:

lncli-ws create

Follow the prompts to complete the LN wallet creation process.

If successful you should see the following message:

lnd successfully initialized!

Switch back to the terminal window where your lnd is running. You should now see something like this:

2018-10-02 22:29:07.280 [INF] LNWL: Opened wallet
2018-10-02 22:29:07.340 [INF] LTND: Primary chain is set to: bitcoin
2018-10-02 22:29:08.233 [INF] LNWL: The wallet has been unlocked without a time limit
2018-10-02 22:29:08.233 [INF] LNWL: Catching up block hashes to height 0, this will take a while...
2018-10-02 22:29:08.237 [INF] LTND: LightningWallet opened
2018-10-02 22:29:08.242 [INF] LNWL: Caught up to height 0
2018-10-02 22:29:08.243 [INF] HSWC: Restoring in-memory circuit state from disk
2018-10-02 22:29:08.244 [INF] LNWL: Done catching up block hashes
2018-10-02 22:29:08.245 [INF] HSWC: Payment circuits loaded: num_pending=0, num_open=0
2018-10-02 22:29:08.246 [INF] LNWL: Started rescan from block 683e86bd5c6d110d91b94b97137ba6bfe02dbbdb8e3dff722a669b5d69d77af6 (height 0) for 0 addresses
2018-10-02 22:29:08.247 [INF] LNWL: Catching up block hashes to height 0, this might take a while
2018-10-02 22:29:08.249 [INF] LNWL: Done catching up block hashes
2018-10-02 22:29:08.249 [INF] LNWL: Finished rescan for 0 addresses (synced to block 683e86bd5c6d110d91b94b97137ba6bfe02dbbdb8e3dff722a669b5d69d77af6, height 0)
2018-10-02 22:29:08.250 [INF] RPCS: RPC server listening on
2018-10-02 22:29:08.250 [INF] RPCS: gRPC proxy started at
2018-10-02 22:29:08.302 [INF] HSWC: Starting HTLC Switch
2018-10-02 22:29:08.302 [INF] NTFN: New block epoch subscription
2018-10-02 22:29:08.302 [INF] NTFN: New block epoch subscription
2018-10-02 22:29:08.302 [INF] NTFN: New block epoch subscription
2018-10-02 22:29:08.302 [INF] DISC: Authenticated Gossiper is starting
2018-10-02 22:29:08.302 [INF] NTFN: New block epoch subscription
2018-10-02 22:29:08.302 [INF] BRAR: Starting contract observer, watching for breaches.
2018-10-02 22:29:08.302 [INF] CRTR: FilteredChainView starting
2018-10-02 22:29:08.350 [INF] CRTR: Filtering chain using 0 channels active
2018-10-02 22:29:08.350 [INF] CRTR: Prune tip for Channel Graph: height=0, hash=683e86bd5c6d110d91b94b97137ba6bfe02dbbdb8e3dff722a669b5d69d77af6
2018-10-02 22:29:08.351 [INF] CMGR: Server listening on [::]:9735
2018-10-02 22:29:08.352 [INF] SRVR: Auto peer bootstrapping is disabled

Funding Your New Wallet

Create a new bitcoin address:

lncli-ws newaddress np2wkh

Note that the address type here is important - np2wkh ("Pay to nested witness key hash").

Example output:

    "address": ""

Copy and paste this address to the IRC channel.

The workshop organizer should now re-start the btcd node with the address as the recipient "mining" address:

btcd --miningaddr=

Then the organizer should generate new blocks using the btcctl utility:

btcctl generate 100

This will generate 100 new blocks.

Example output:

  "... more block hashes omitted"

Normally blocks are mined about once every 10 minutes. But we don't have that kind of time, so we use the above command to generate blocks on-demand as we need them.

Now check your wallet balance:

lncli-ws walletbalance

Example output:

    "total_balance": "5000000000",
    "confirmed_balance": "5000000000",
    "unconfirmed_balance": "0"
  • The amounts shown are in satoshis
  • 1 bitcoin = 100 million satoshis

The above steps should be repeated for each workshop participant.

Opening a Channel

Before you can open channels, you will need to be connected to other lightning nodes ("peers"). To see your current peer connections:

lncli-ws listpeers

Since you haven't connected to any peers yet, the result will be an empty array:

  "peers": []

To connect to "Bob":

lncli-ws connect 0200eacbb299639eb19f4afd39d572e87398062e500e6b63ceb1ebded31fddd3b7@

Now when you list your peers, you should see Bob's node listed:

lncli-ws listpeers


    "peers": [
            "pub_key": "0200eacbb299639eb19f4afd39d572e87398062e500e6b63ceb1ebded31fddd3b7",
            "address": "",
            "bytes_sent": "279",
            "bytes_recv": "279",
            "sat_sent": "0",
            "sat_recv": "0",
            "inbound": false,
            "ping_time": "0"

But all we've done so far is make two lightning nodes aware of each other. We haven't opened any channels yet.

Try to open a channel with your new peer:

lncli-ws openchannel --node_key= --local_amt=1000000

Each channel has two sides ("local" and "remote"). The local_amt argument in the above command is how much you want to add to the channel on the local (your) side.

Example output on success:

  "funding_txid": "41f045e8d3a33cceff237183e37eeabff3aea0cf4d64e28238807afe5df2dfa5"

If you see the following error:

[lncli] rpc error: code = Unknown desc = not enough witness outputs to create funding transaction, need 0.01 BTC only have 0 BTC  available

Then you need to go back to the Create and Fund Your Wallet step above and check that you are using the correct address type.

If you see an error similar to the following:

[lncli] rpc error: code = Unknown desc = has witness data, but segwit isn't active yet

This means that the block height of the simnet blockchain is not high enough. The threshold for segwit activation is 300 blocks on simnet.

When your lightning node tries to open a new channel, it creates and broadcasts a special bitcoin transaction. Before the channel is considered "open", this transaction needs to be confirmed. The default number of confirmations (blocks) is 6. Whoever is controlling the btcd node should generate 6 new blocks:

btcctl generate 6

Check that the channel was created:

lncli-ws listchannels

You should see your new channel:

    "channels": [
            "active": true,
            "remote_pubkey": "0200eacbb299639eb19f4afd39d572e87398062e500e6b63ceb1ebded31fddd3b7",
            "channel_point": "a8ea16394e1c759f57b3486eea33e3977b0a39d7ea70f409c3519bd581b52573:0",
            "chan_id": "1181974999924736",
            "capacity": "1000000",
            "local_balance": "990950",
            "remote_balance": "0",
            "commit_fee": "9050",
            "commit_weight": "600",
            "fee_per_kw": "12500",
            "unsettled_balance": "0",
            "total_satoshis_sent": "0",
            "total_satoshis_received": "0",
            "num_updates": "0",
            "pending_htlcs": [
            "csv_delay": 144,
            "private": false

Super! With your newly opened channel, you can start sending and receiving lightning payments.

Sending and Receiving Payments

The first step in receiving a payment via the Lightning Network, is to generate an invoice. Use the following command to create your first invoice:

lncli-ws addinvoice --amt=10000

Example output:

  "r_hash": "a9b22eca10de08426f11f3f59b8a733f1af831a699c1b3f6ca632533239dc1dd",
  "pay_req": "lnsb1pd8pxdzpp54xezajssmcyyymc3706ehznn8ud0svdxn8qm8ak2vvjnxguac8wsdqqcqzyse0qkh2fdn4adwlz598s4v9l2ulner3jalncsjf33za0r3hksv2u3m7vw2663ypaqcc4fjsuzeh5n5hfsqyggwk3rzp6neng4hza8stgp4aaszp"

The pay_req field is what we will share with our customer (or whoever will pay the invoice).

Share the above payment request invoice via the IRC chat so that one of the other workshop participants can send you a payment.

Some other participant should run the following command to send the payment:

lncli-ws payinvoice 

But wait, something went wrong:

    "payment_error": "unable to route payment to destination: TemporaryChannelFailure: insufficient capacity in available outgoing links: need 9002018 mSAT, max available is 1312000 mSAT",
    "payment_preimage": "",
    "payment_route": null

We are seeing this error because Bob doesn't have any funds on its side of your channel. In order for the payment to be successfully routed from the other participant to you via Bob, his node must have a local balance equal to or greater than the requested amount. Here is a series of diagrams to illustrate:

+ ----- +                      + --- +                      + --------- +
| payor | <-- 1000000 -- 0 --> | Bob | <-- 0 -- 1000000 --> | recipient |
+ ----- +                      + --- +                      + --------- +

The next step of the failing payment looks like this:

+ ----- +                         + --- +                      + --------- +
| payor | <-- 990000 -- 10000 --> | Bob | <-- 0 -- 1000000 --> | recipient |
+ ----- +                         + --- +                      + --------- +

And then where the payment finally fails:

+ ----- +                         + --- +                            + --------- +
| payor | <-- 990000 -- 10000 --> | Bob | <-- (10000) -- 1010000 --> | recipient |
+ ----- +                         + --- +       !!!                  + --------- +

Payments are routed by transferring value from one side of a channel to another so that the transacted amount is effectively moved through the network until it reaches its final destination. You can only ever transfer value between the two peers involved in a channel by manipulating the local and remote balances in that channel. It is not possible to transfer value directly between channels. If one side of a channel does not have enough value to transfer to the other side, the routing will fail - as in the example above.

To fix this, you must first send some funds to Bob.

Whoever is controlling Bob's node should create a second payment request:

lncli-ws addinvoice --amt=10000

Once they've shared the payment request via IRC, go ahead and pay it:

lncli-ws payinvoice 

Check the balances in your channel to see that the payment was sent:

lncli-ws listchannels

Now you should see that the remote balance is enough for your previous payment request to be routed.

    "channels": [
            "active": true,
            "remote_pubkey": "0200eacbb299639eb19f4afd39d572e87398062e500e6b63ceb1ebded31fddd3b7",
            "channel_point": "a8ea16394e1c759f57b3486eea33e3977b0a39d7ea70f409c3519bd581b52573:0",
            "chan_id": "1181974999924736",
            "capacity": "1000000",
            "local_balance": "980950",
            "remote_balance": "10000",
            "commit_fee": "9050",
            "commit_weight": "724",
            "fee_per_kw": "12500",
            "unsettled_balance": "0",
            "total_satoshis_sent": "10000",
            "total_satoshis_received": "0",
            "num_updates": "2",
            "pending_htlcs": [
            "csv_delay": 144,
            "private": false

Ask another workshop participant to try to pay your invoice. This time it should be successful. Cool!

That's it for this workshop. The remaining sections are part of the setup guide for the workshop organizer.

Thanks for participating!

Setup Private Bitcoin Network

In order to create a private bitcoin network, you will need to setup at least one bitcoin node. In this workshop, we will be using btcd. Steps to install btcd:

go get -v -u
git clone $GOPATH/src/
cd $GOPATH/src/
glide install
go install . ./cmd/...

Create btcd configuration file. RPC usernames and passwords will be "randomly" generated when you create the file:

mkdir -p ~/.btcd;
cat > ~/.btcd/btcd.conf << EOL
rpclisten=$(host=($(hostname -I)); echo ${host[0]})
rpcuser=$(date '+%s%N-ln-workshop' | sha256sum | head -c 20)
rpcpass=$(date '+%s%N-ln-workshop' | sha256sum | head -c 32)
rpclimituser=$(date '+%s%N-ln-workshop' | sha256sum | head -c 20)
rpclimitpass=$(date '+%s%N-ln-workshop' | sha256sum | head -c 32)

Create the btcctl configuration file:

mkdir -p ~/.btcctl;
cat > ~/.btcctl/btcctl.conf << EOL
$(cat ~/.btcd/btcd.conf | grep rpcuser=)
$(cat ~/.btcd/btcd.conf | grep rpcpass=)

Start btcd:


You should see something like the following:

2018-10-02 21:58:48.039 [INF] BTCD: Version 0.12.0-beta
2018-10-02 21:58:48.039 [INF] BTCD: Loading block database from '/home/user/.btcd/data/simnet/blocks_ffldb'
2018-10-02 21:58:48.045 [INF] BTCD: Block database loaded
2018-10-02 21:58:48.055 [INF] INDX: Transaction index is enabled
2018-10-02 21:58:48.055 [INF] INDX: cf index is enabled
2018-10-02 21:58:48.056 [DBG] INDX: Current internal block ID: 0
2018-10-02 21:58:48.056 [DBG] INDX: Current transaction index tip (height -1, hash 0000000000000000000000000000000000000000000000000000000000000000)
2018-10-02 21:58:48.056 [DBG] INDX: Current committed filter index tip (height -1, hash 0000000000000000000000000000000000000000000000000000000000000000)
2018-10-02 21:58:48.056 [INF] INDX: Catching up indexes from height -1 to 0
2018-10-02 21:58:48.056 [INF] INDX: Indexes caught up to height 0
2018-10-02 21:58:48.056 [INF] CHAN: Chain state (height 0, hash 683e86bd5c6d110d91b94b97137ba6bfe02dbbdb8e3dff722a669b5d69d77af6, totaltx 1, work 2)
2018-10-02 21:58:48.073 [INF] AMGR: Loaded 0 addresses from file '/home/user/.btcd/data/simnet/peers.json'
2018-10-02 21:58:48.073 [INF] RPCS: RPC server listening on
2018-10-02 21:58:48.073 [INF] CMGR: Server listening on
2018-10-02 21:58:48.073 [INF] CMGR: Server listening on [::]:18555

If you have a firewall enabled (always a good idea!), then you will need to allow connections to the btcd node's RPC port:

sudo ufw allow 18556

Print your local area network IP address:

hostname -I

And the credentials for the limited RPC user:

cat ~/.btcd/btcd.conf | grep rpclimit

And finally the btcd node's RPC certificate:

cat ~/.btcd/rpc.cert

Share the output of the above commands with the workshop's IRC channel, so that the workshop attendees can finish the configuration of their lnd nodes.

For the RPC TLS certificate, create a "gist" and share the link instead of posting directly to the IRC chat.

Follow the participant instructions (links at the top of the page), to setup your lnd node and to create and fund its bitcoin wallet. Your lnd node will act as "Bob" during this workshop.

Gotcha's for Organizer

There are some edge cases that can cause problems and waste time:

  • If you've already had your btcd node setup once before and you change its listener IP address, you will need to re-generate the RPC TLS certificate. Delete the current certificate (and key file) then restart btcd.

More Resources

To learn more about the Lightning Network, have a look at the following additional resource(s):