- Introduction
- For participants:
- For the organizer:
- More Resources
Introduction
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 webchat.freenode.net
- 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:
wget https://storage.googleapis.com/golang/go1.10.4.linux-amd64.tar.gz
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"
PATH=\$PATH:\$GOROOT/bin:\$GOPATH/bin
EOL
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 github.com/lightningnetwork/lnd
cd $GOPATH/src/github.com/lightningnetwork/lnd
make && make install
Create an lnd configuration file:
mkdir -p ~/.lnd;
cat > ~/.lnd/lnd.conf << EOL
bitcoin.active=1
bitcoin.simnet=true
bitcoin.node=btcd
btcd.rpchost=PROVIDED_BY_ORGANIZER
btcd.rpcuser=PROVIDED_BY_ORGANIZER
btcd.rpcpass=PROVIDED_BY_ORGANIZER
btcd.rpccert=$HOME/.btcd/rpc.cert
debuglevel=debug
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"
EOL
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
PROVIDED_BY_ORGANIZER
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
:
lnd-ws
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 127.0.0.1:10009
2018-10-02 22:03:15.732 [INF] RPCS: password gRPC proxy started at 127.0.0.1:8080
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 127.0.0.1:10009
2018-10-02 22:29:08.250 [INF] RPCS: gRPC proxy started at 127.0.0.1:8080
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:
[
"68d12bead9ac4a87599582d5186bf08634c9dfe86678d123b27d7d682ecd39df",
"14cd20d79a0b2786b6a24710e75822eadd949f57cac93ea0dc8abcf858f7bf18",
"67e5d6ceb6db58d334a195ab85613753aa909a2783c61c600320733614d6c7c7",
"4e92a4c34792ac9394efb8470d6251656b09ae1e9d8bdfb95f5f7a69569ce925",
"5ba8cbc7d51c80372aa48512fc81760f48f26167df518e6845ace3fab0978882",
"... 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@159.89.23.5
Now when you list your peers, you should see Bob's node listed:
lncli-ws listpeers
Result:
{
"peers": [
{
"pub_key": "0200eacbb299639eb19f4afd39d572e87398062e500e6b63ceb1ebded31fddd3b7",
"address": "159.89.23.5:9735",
"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 github.com/Masterminds/glide
git clone https://github.com/roasbeef/btcd $GOPATH/src/github.com/roasbeef/btcd
cd $GOPATH/src/github.com/roasbeef/btcd
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
simnet=1
txindex=1
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)
rpcmaxclients=100
rpcmaxwebsockets=300
debuglevel=debug
EOL
Create the btcctl configuration file:
mkdir -p ~/.btcctl;
cat > ~/.btcctl/btcctl.conf << EOL
simnet=1
$(cat ~/.btcd/btcd.conf | grep rpcuser=)
$(cat ~/.btcd/btcd.conf | grep rpcpass=)
EOL
Start btcd:
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 10.0.0.1:18556
2018-10-02 21:58:48.073 [INF] CMGR: Server listening on 0.0.0.0:18555
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):
- https://dev.lightning.community/ - Community-driven website with many guides and detailed technical documentation about the LN protocol
- https://github.com/ElementsProject/lightning - Alternate LN implementation written in C
- https://github.com/ACINQ/eclair - Alternate LN implementation written in Scala