From f94fe3fd9362920802b5643154790e7938052c83 Mon Sep 17 00:00:00 2001 From: "Andreas M. Antonopoulos" Date: Sun, 12 Sep 2021 16:12:43 +0200 Subject: [PATCH] Docker example fixes, upgrades and ch4 text edits The docker containers have been improved and updated. The payment demo script can be rerun and is resilient to errors and delays. The docker mini-tutotial and installation instructions have been moved to a new appendix --- 04_node_client.asciidoc | 709 +++++++++--------- appendix_docker_basics.asciidoc | 113 +++ code/docker/Makefile | 126 +++- code/docker/bitcoind/Dockerfile | 55 +- code/docker/bitcoind/bitcoind-entrypoint.sh | 27 +- code/docker/bitcoind/cli | 5 + code/docker/bitcoind/mine.sh | 13 +- code/docker/c-lightning/Dockerfile | 45 +- .../c-lightning/c-lightning-entrypoint.sh | 1 - code/docker/c-lightning/cli | 5 + code/docker/c-lightning/fund-c-lightning.sh | 8 +- code/docker/c-lightning/logtail.sh | 1 + code/docker/c-lightning/wait-for-bitcoind.sh | 4 +- code/docker/check-versions.sh | 3 +- code/docker/eclair/Dockerfile | 42 +- code/docker/eclair/cli | 5 + code/docker/eclair/eclair-entrypoint.sh | 1 - code/docker/eclair/eclair/eclair.conf | 9 +- code/docker/eclair/wait-for-bitcoind.sh | 4 +- code/docker/lnd/Dockerfile | 61 +- code/docker/lnd/cli | 5 + code/docker/lnd/lnd-entrypoint.sh | 1 - code/docker/lnd/wait-for-bitcoind.sh | 4 +- code/docker/run-payment-demo.sh | 177 +++++ code/docker/setup-channels.sh | 37 - 25 files changed, 942 insertions(+), 519 deletions(-) create mode 100644 appendix_docker_basics.asciidoc create mode 100644 code/docker/bitcoind/cli create mode 100644 code/docker/c-lightning/cli create mode 100644 code/docker/eclair/cli create mode 100644 code/docker/lnd/cli create mode 100755 code/docker/run-payment-demo.sh delete mode 100644 code/docker/setup-channels.sh diff --git a/04_node_client.asciidoc b/04_node_client.asciidoc index 46c7bf4..df8ac9b 100644 --- a/04_node_client.asciidoc +++ b/04_node_client.asciidoc @@ -64,151 +64,67 @@ The book's repository contains a collection of Docker containers that can be use Docker containers are often optimized to be small, i.e. occupy the minimum disk space. However, in this book we are using containers to _standardize_ the environment and make it consistent for all readers. Furthermore, these containers are not meant to be used to run services in the background. Instead, they are meant to be used to test the examples and learn by interacting with the software. For these reasons, the containers are quite large and come with a lot of development tools and utilities. Commonly the Alpine distribution is used for Linux containers due to their reduced size. Nonetheless, we provide containers built on Ubuntu because more developers are familiar with Ubuntu, and this familiarity is more important to us than size. +The installation of use of Docker and its commands is detailed in <>. If you are unfamiliar with Docker, now is a good time to quickly review that section. + You can find the latest container definitions and build configurations in the book's repository under the +code/docker+ folder. Each container is in a separate folder as can be seen below: [tree] ---- -$ tree -F --charset=asciii code +$ tree -F --charset=asciii code/docker ---- [docker-dir-list] ---- -code -`-- docker/ - |-- Makefile - |-- bitcoind/ - | |-- Dockerfile - | |-- bashrc - | |-- bitcoind/ - | | |-- bitcoin.conf - | | `-- keys/ - | | |-- demo_address.txt - | | |-- demo_mnemonic.txt - | | `-- demo_privkey.txt - | |-- bitcoind-entrypoint.sh - | `-- mine.sh* - |-- c-lightning/ - | |-- Dockerfile - | |-- bashrc - | |-- c-lightning-entrypoint.sh - | |-- fund-c-lightning.sh - | |-- lightningd/ - | | `-- config - | |-- logtail.sh - | `-- wait-for-bitcoind.sh - |-- docker-compose.yml - |-- eclair/ - | |-- Dockerfile - | |-- bashrc - | |-- eclair/ - | | `-- eclair.conf - | |-- eclair-entrypoint.sh - | |-- logtail.sh - | `-- wait-for-bitcoind.sh - |-- lnd/ - | |-- Dockerfile - | |-- bashrc - | |-- fund-lnd.sh - | |-- lnd/ - | | `-- lnd.conf - | |-- lnd-entrypoint.sh - | |-- logtail.sh - | `-- wait-for-bitcoind.sh - `-- setup-channels.sh ----- - -==== Installing Docker - -Before we begin, you should install the Docker container system on your computer. Docker is an open system that is distributed for free as a _Community Edition_ for many different operating systems including Windows, Mac OS and Linux. The Windows and Mac versions are called _Docker Desktop_ and consist of a GUI desktop application and command-line tools. The Linux version is called _Docker Engine_ and is comprised of a server daemon and command-line tools. We will be using the command-line tools, which are identical across all platforms. - -Go ahead and install Docker for your operating system by following the instructions to _"Get Docker"_ from the Docker website found here: - -https://docs.docker.com/get-docker/ - -Select your operating system from the list and follow the installation instructions. - -[TIP] -==== -If you install on Linux, follow the post-installation instructions to ensure you can run Docker as a regular user instead of user _root_. Otherwise, you will need to prefix all +docker+ commands with +sudo+, running them as root like: +sudo docker+. -==== - -Once you have Docker installed, you can test your installation by running the demo container +hello-world+ like this: - -[docker-hello-world] ----- -$ docker run hello-world - -Hello from Docker! -This message shows that your installation appears to be working correctly. - -[...] ----- - -==== Basic Docker commands - -In this chapter, we use Docker quite extensively. We will be using the following Docker commands and arguments: - -*Building a container* - ----- -docker build [-t tag] [directory] ----- - -...where +tag+ is how we identify the container we are building, and +directory+ is where the container's "context" (folders and files) and definition file (+Dockerfile+) are found. - -*Running a container* - ----- -docker run -it [--network netname] [--name cname] tag ----- - -...where +netname+ is the name of a Docker network, +cname+ is the name we choose for this container instance and +tag+ is the name tag we gave the container when we built it. - -*Executing a command in a container* - ----- -docker exec cname command ----- - -...where +cname+ is the name we gave the container in the +run+ command, and +command+ is an executable or script that we want to run inside the container. - -*Stopping and starting a container* - -In most cases, if we are running a container in an _interactive_ as well as _terminal_ mode, i.e. with the +i+ and +t+ flags (combined as +-it+) set, the container can be stopped by simply pressing +CTRL-C+ or by exiting the shell with +exit+ or +CTRL-D+. If a container does not terminate, you can stop it from another terminal like this: - ----- -docker stop cname ----- - -To resume an already existing container use the `start` command, like so: - ----- -docker start cname ----- - -*Deleting a container by name* - -If you name a container instead of letting Docker name it randomly, you cannot reuse that name until the container is deleted. Docker will return an error like this: -[source,bash] ----- -docker: Error response from daemon: Conflict. The container name "/bitcoind" is already in use... ----- - -To fix this, delete the existing instance of the container: - ----- -docker rm cname ----- - -...where +cname+ is the name assigned to the container (+bitcoind+ in the example error message) - -*List running containers* - ----- -docker ps ----- - -These basic Docker commands will be enough to get you started and will allow you to run all the examples in this chapter. Let's see them in action in the following sections. +code/docker +|-- bitcoind/ +| |-- bashrc +| |-- bitcoind/ +| | |-- bitcoin.conf +| | `-- keys/ +| | |-- demo_address.txt +| | |-- demo_mnemonic.txt +| | `-- demo_privkey.txt +| |-- bitcoind-entrypoint.sh +| |-- cli +| |-- Dockerfile +| `-- mine.sh* +|-- c-lightning/ +| |-- bashrc +| |-- cli +| |-- c-lightning-entrypoint.sh +| |-- devkeys.pem +| |-- Dockerfile +| |-- fund-c-lightning.sh +| |-- lightningd/ +| | `-- config +| |-- logtail.sh +| `-- wait-for-bitcoind.sh +|-- eclair/ +| |-- bashrc +| |-- cli +| |-- Dockerfile +| |-- eclair/ +| | `-- eclair.conf +| |-- eclair-entrypoint.sh +| |-- logtail.sh +| `-- wait-for-bitcoind.sh +|-- lnd/ +| |-- bashrc +| |-- cli +| |-- Dockerfile +| |-- fund-lnd.sh +| |-- lnd/ +| | `-- lnd.conf +| |-- lnd-entrypoint.sh +| |-- logtail.sh +| `-- wait-for-bitcoind.sh +|-- check-versions.sh +|-- docker-compose.yml +|-- Makefile +`-- run-payment-demo.sh* +---- + +As we will see in the next few sections, you can build these containers locally, or you can pull them from the book's repository on _Docker Hub_. The following sections will assume that you have installed Docker and are familiar with the basic use of the +docker+ command. === Bitcoin Core and regtest @@ -218,60 +134,77 @@ Installing a full Bitcoin node and syncing the Bitcoin blockchain is outside the A Bitcoin node can be operated in _regtest_ mode, where the node creates a local simulated Bitcoin blockchain for testing purposes. In the following examples, we will be using the +regtest+ mode to allow us to demonstrate Lightning without having to synchronize a Bitcoin node or risk any funds. -The container for Bitcoin Core is +bitcoind+. It is configured to run Bitcoin Core in +regtest+ mode and to mine a new block every 10 seconds. Its RPC port is exposed on port 18443 and accessible for RPC calls with the username +regtest+ and the password +regtest+. You can also access it with an interactive shell and run +bitcoin-cli+ commands locally. +The container for Bitcoin Core is +bitcoind+. It is configured to run Bitcoin Core in +regtest+ mode and to mine 6 new blocks every 10 seconds. Its RPC port is exposed on port 18443 and accessible for RPC calls with the username +regtest+ and the password +regtest+. You can also access it with an interactive shell and run +bitcoin-cli+ commands locally. ===== Building the Bitcoin Core container -Let's start by building and running the +bitcoind+ container. First, we use the +docker build+ command to build it: +Let's prepare the +bitcoind+ container. The easiest way is to pull the latest container from _Docker Hub_: [source,bash] ---- -$ cd code/docker -$ docker build -t lnbook/bitcoind bitcoind -Sending build context to Docker daemon 12.29kB -Step 1/25 : FROM ubuntu:20.04 AS bitcoind-base - ---> c3c304cb4f22 -Step 2/25 : RUN apt update && apt install -yqq curl gosu jq bash-completion - +$ docker pull lnbook/bitcoind +Using default tag: latest +latest: Pulling from lnbook/bitcoind +35807b77a593: Pull complete +e1b85b9c5571: Pull complete [...] +288f1cc78a00: Pull complete +Digest: sha256:861e7e32c9ad650aa367af40fc5acff894e89e47aff4bd400691ae18f1b550e2 +Status: Downloaded newer image for lnbook/bitcoind:latest +docker.io/lnbook/bitcoind:latest -Step 25/25 : CMD ["/usr/local/bin/mine.sh"] - ---> Using cache - ---> 758051998e72 -Successfully built 758051998e72 -Successfully tagged lnbook/bitcoind:latest ---- -===== Running the Bitcoin Core container +Alternatively, you can build the container yourself from the local container definition that is in +code/docker/bitcoind/Dockerfile+. -Next, let's run the +bitcoind+ container and have it mine some blocks. We use the +docker run+ command, with the flags for _interactive (i)_ and _terminal (t)_, and the +name+ argument to give the running container a custom name: +[NOTE] +==== +You don't need to build the container if you used the "pull" command previously to pull it from Docker Hub +==== + +Building the container locally will use a bit less of your network bandwidth, but will take more of your CPU time to build. We use the +docker build+ command to build it: [source,bash] ---- +$ cd code/docker $ docker run -it --name bitcoind lnbook/bitcoind Starting bitcoind... Bitcoin Core starting +Waiting for bitcoind to start bitcoind started ================================================ -Importing demo private key +Imported demo private key Bitcoin address: 2NBKgwSWY5qEmfN2Br4WtMDGuamjpuUc5q1 Private key: cSaejkcWwU25jMweWEewRSsrVQq2FGTij1xjXv4x1XvxVRF1ZCr3 ================================================ - +================================================ +Balance: 0.00000000 +================================================ Mining 101 blocks to unlock some bitcoin [ - "579311009cc4dcf9d4cc0bf720bf210bfb0b4950cdbda0670ff56f8856529b39", - ... - "33e0a6e811d6c49219ee848604cedceb0ab161485e1195b1f3576049e4d5deb7" + "34c744207fd4dd32b70bac467902bd8d030fba765c9f240a2e98f15f05338964", + "64d82721c641c378d79b4ff2e17572c109750bea1d4eddbae0b54f51e4cdf23e", + + [...] + + "7a8c53dc9a3408c9ecf9605b253e5f8086d67bbc03ea05819b2c9584196c9294", + "39e61e50e34a9bd1d6eab51940c39dc1ab56c30b21fc28e1a10c14a39b67a1c3", + "4ca7fe9a55b0b767d2b7f5cf4d51a2346f035fe8c486719c60a46dcbe33de51a" ] -Mining 1 block every 10 seconds +Mining 6 blocks every 10 seconds +Balance: 50.00000000 [ - "5974aa6da1636013daeaf730b5772ae575104644b8d6fa034203d2bf9dc7a98b" + "5ce76cc475e40515b67e3c0237d1eef597047a914ba3f59bbd62fc3691849055", + "1ecb27a05ecfa9dfa82a7b26631e0819b2768fe5e6e56c7a2e1078b078e21e9f", + "717ceb8b6c329d57947c950dc5668fae65bddb7fa03203984da9d2069e20525b", + "185fc7cf3557a6ebfc4a8cdd1f94a8fa08ed0c057040cdd68bfb7aee2d5be624", + "59001ae237a3834ebe4f6e6047dcec8fd67df0352ddc70b6b02190f982a60384", + "754c860fe1b9e0e7292e1de96a65eaa78047feb4c72dbbde2a1d224faa1499dd" ] -Balance: 100.00000000 + ---- -As you can see, bitcoind starts up and mines 101 simulated blocks to get the chain started. This is because under the Bitcoin consensus rules, newly mined bitcoin is not spendable until 100 blocks have elapsed. By mining 101 blocks, we make the first block's coinbase spendable. After that initial mining activity, a new block is mined every 10 seconds to keep the chain moving forward. +As you can see, bitcoind starts up and mines 101 simulated blocks to get the chain started. This is because under the Bitcoin consensus rules, newly mined bitcoin is not spendable until 100 blocks have elapsed. By mining 101 blocks, we make the first block's coinbase spendable. After that initial mining activity, six new blocks are mined every 10 seconds to keep the chain moving forward. For now, there are no transactions. But we have some test bitcoin that has been mined in the wallet and is available to spend. When we connect some Lightning nodes to this chain, we will send some bitcoin to their wallets so that we can open some Lightning channels between the Lightning nodes. @@ -293,34 +226,35 @@ root@e027fd56e31a:/bitcoind# Running the interactive shell puts us "inside" the container. It logs in as user +root+, as we can see from the prefix +root@+ in the new shell prompt +root@e027fd56e31a:/bitcoind#+. If we issue the +ps x+ command to see what processes are running, we see both +bitcoind+ and the script +mine.sh+ are running in the background. To exit this shell, type +CTRL-D+ or +exit+ and you will be returned to your operating system prompt. -Instead of running an interactive shell, we can also issue a single command that is executed inside the container. In the following example we run the +bitcoin-cli+ command to obtain information about the current blockchain state: +Instead of running an interactive shell, we can also issue a single command that is executed inside the container. For convenience, the +bitcoin-cli+ command has an alias "cli" that passes the correct configuration. So let's run it to ask Bitcoin Code about the blockchain. We run +cli getblockchaininfo+: [source,bash] ---- -$ docker exec bitcoind bitcoin-cli -datadir=/bitcoind getblockchaininfo +$ docker exec bitcoind cli getblockchaininfo { "chain": "regtest", - "blocks": 149, - "headers": 149, - "bestblockhash": "35e97bf507607be010be1daa10152e99535f7b0f9882d0e588c0037d8d9b0ba1", - "difficulty": 4.656542373906925e-10, - [...] + "blocks": 131, + "headers": 131, + "bestblockhash": "2cf57aac35365f52fa5c2e626491df634113b2f1e5197c478d57378e5a146110", + +[...] + "warnings": "" } -$ + ---- -As you can see, we need to tell +bitcoin-cli+ where the bitcoind data directory is by using the +datadir+ argument. We can then issue RPC commands to the Bitcoin Core node and get JSON encoded results. +The +cli+ command in the bitcoind container allows us to issue RPC commands to the Bitcoin Core node and get JSON-encoded results. -All our docker containers have a command-line JSON encoder/decoder named +jq+ preinstalled. +jq+ helps us to process JSON-formatted data via the command-line or from inside scripts. You can send the JSON output of any command to +jq+ using the +|+ character. This character as well as this operation is called a "pipe". Let's apply a +pipe+ and +jq+ to the previous command as follows: +Additionally, all our docker containers have a command-line JSON encoder/decoder named +jq+ preinstalled. +jq+ helps us to process JSON-formatted data via the command-line or from inside scripts. You can send the JSON output of any command to +jq+ using the +|+ character. This character as well as this operation is called a "pipe". Let's apply a +pipe+ and +jq+ to the previous command as follows: [source,bash] ---- -$ docker exec bitcoind bash -c "bitcoin-cli -datadir=/bitcoind getblockchaininfo | jq .blocks" -189 +$ docker exec bitcoind bash -c "cli getblockchaininfo | jq .blocks" +197 ---- -+jq .blocks+ instructs the +jq+ JSON decoder to extract the field +blocks+ from the +getblockchaininfo+ result. In our case, it extracts and prints the value of 189 which we could use in a subsequent command. ++jq .blocks+ instructs the +jq+ JSON decoder to extract the field +blocks+ from the +getblockchaininfo+ result. In our case, it extracts and prints the value of 197 which we could use in a subsequent command. As you will see in the following sections, we can run several containers at the same time and then interact with them individually. We can issue commands to extract information such as the Lightning node public key or to take actions such as opening a Lightning channel to another node. The +docker run+ and +docker exec+ commands together with +jq+ for JSON decoding are all we need to build a working Lightning Network that mixes many different node implementations. This enables us to try out diverse experiments on our own computer. @@ -336,24 +270,44 @@ In the following sections, we will build a Docker container that runs a c-lightn The c-lightning software distribution has a Docker container, but it is designed for running c-lightning in production systems and along side a bitcoind node. We will be using a somewhat simpler container configured to run c-lightning for demonstration purposes. -We start by building the c-lightning Docker container from the book's files which you previously downloaded into a directory named +lnbook+. As before, we will use the +docker build+ command in the +code/docker+ sub-directory. We will tag the container image with the tag +lnbook/c-lightning+ like this: +Let's pull the c-lightning container from the book's Docker Hub repository: + +[source,bash] +---- +$ docker pull lnbook/c-lightning +Using default tag: latest +latest: Pulling from lnbook/c-lightning + +[...] + +Digest: sha256:bdefcefe8a9712e7b3a236dcc5ab12d999c46fd280e209712e7cb649b8bf0688 +Status: Downloaded image for lnbook/c-lightning:latest +docker.io/lnbook/c-lightning:latest + +---- + + +Alternatively, we can build the c-lightning Docker container from the book's files which you previously downloaded into a directory named +lnbook+. As before, we will use the +docker build+ command in the +code/docker+ sub-directory. We will tag the container image with the tag +lnbook/c-lightning+ like this: [source,bash] ---- $ cd code/docker $ docker build -t lnbook/c-lightning c-lightning -Sending build context to Docker daemon 10.24kB -Step 1/21 : FROM lnbook/bitcoind AS c-lightning-base - ---> 758051998e72 -Step 2/21 : RUN apt update && apt install -yqq software-properties-common +Sending build context to Docker daemon 91.14kB +Step 1/34 : ARG OS=ubuntu +Step 2/34 : ARG OS_VER=focal +Step 3/34 : FROM ${OS}:${OS_VER} as os-base + ---> fb52e22af1b0 -[...] + [...] -Step 21/21 : CMD ["/usr/local/bin/logtail.sh"] - ---> Using cache - ---> e63f5aaa2b16 -Successfully built e63f5aaa2b16 +Step 34/34 : CMD ["/usr/local/bin/logtail.sh"] + ---> Running in 8d3d6c8799c5 +Removing intermediate container 8d3d6c8799c5 + ---> 30b6fd5d7503 +Successfully built 30b6fd5d7503 Successfully tagged lnbook/c-lightning:latest + ---- Our container is now built and ready to run. However, before we run the c-lightning container, we need to start the bitcoind container in another terminal as c-lightning depends on bitcoind. We will also need to set up a Docker network that allows the containers to connect to each other as if residing on the same local area network. @@ -367,6 +321,7 @@ Docker containers can "talk" to each other over a virtual local area network man Once a Docker network is set up, Docker will activate the network on our local computer every time Docker starts, e.g. after rebooting. So we only need to set up a network once by using the +docker network create+ command. The network name itself is not important, but it has to be unique on our computer. By default, Docker has three networks named +host+, +bridge+, and +none+. We will name our new network +lnbook+ and create it like this: +[source,bash] ---- $ docker network create lnbook ad75c0e4f87e5917823187febedfc0d7978235ae3e88eca63abe7e0b5ee81bfb @@ -384,6 +339,7 @@ As you can see, running +docker network ls+ gives us a listing of the Docker net The next step is to start the bitcoind and c-lightning containers and connect them to the +lnbook+ network. To run a container in a specific network, we must pass the +network+ argument to +docker run+. To make it easy for containers to find each other, we will also give each one a name with the +name+ argument. We start bitcoind like this: +[source,bash] ---- $ docker run -it --network lnbook --name bitcoind lnbook/bitcoind ---- @@ -396,45 +352,34 @@ $ docker run -it --network lnbook --name c-lightning lnbook/c-lightning Waiting for bitcoind to start... Waiting for bitcoind to mine blocks... Starting c-lightning... -[...] +2021-09-12T13:14:50.434Z UNUSUAL lightningd: Creating configuration directory /lightningd/regtest Startup complete Funding c-lightning wallet -{"result":"e1a392ce2c6af57f8ef1550ccb9a120c14b454da3a073f556b55dc41592621bb","error":null,"id":"c-lightning-container"} -[...] -2020-06-22T14:26:09.802Z DEBUG lightningd: Opened log file /lightningd/lightningd.log +8a37a183274c52d5a962852ba9f970229ea6246a096ff1e4602b57f7d4202b31 +lightningd: Opened log file /lightningd/lightningd.log +lightningd: Creating configuration directory /lightningd/regtest +lightningd: Opened log file /lightningd/lightningd.log ---- The c-lightning container starts up and connects to the bitcoind container over the Docker network. First, our c-lightning node will wait for bitcoind to start and then it will wait until bitcoind has mined some bitcoin into its wallet. Finally, as part of the container startup, a script will send an RPC command to the bitcoind node which creates a transaction that funds the c-lightning wallet with 10 test BTC. Now our c-lightning node is not only running, but it even has some test bitcoin to play with! -As we demonstrated with the bitcoind container, we can issue commands to our c-lightning container in another terminal in order to extract information, open channels etc. The command that allows us to issue command-line instructions to the c-lightning node is called +lightning-cli+. To get the node info use the following +docker exec+ command in another terminal window: +As we demonstrated with the bitcoind container, we can issue commands to our c-lightning container in another terminal in order to extract information, open channels etc. The command that allows us to issue command-line instructions to the c-lightning node is called +lightning-cli+. This +lightning-cli+ command is also aliased as +cli+ inside this container. To get the c-lightning node's info use the following +docker exec+ command in another terminal window: [source,bash] ---- -$ docker exec c-lightning lightning-cli getinfo +$ docker exec c-lightning cli getinfo { - "id": "025656e4ef0627bc87638927b8ad58a0e07e8d8d6e84a5699a5eb27b736d94989b", - "alias": "HAPPYWALK", - "color": "025656", + "id": "026ec53cc8940df5fed5fa18f8897719428a15d860ff4cd171fca9530879c7499e", + "alias": "IRATEARTIST", + "color": "026ec5", "num_peers": 0, "num_pending_channels": 0, - "num_active_channels": 0, - "num_inactive_channels": 0, - "address": [], - "binding": [ - { - "type": "ipv6", - "address": "::", - "port": 9735 - }, - { - "type": "ipv4", - "address": "0.0.0.0", - "port": 9735 - } - ], - "version": "0.8.2.1", - "blockheight": 140, + +[...] + + "version": "0.10.1", + "blockheight": 221, "network": "regtest", "msatoshi_fees_collected": 0, "fees_collected_msat": "0msat", @@ -445,7 +390,12 @@ $ docker exec c-lightning lightning-cli getinfo We now have our first Lightning node running on a virtual network and communicating with a test Bitcoin blockchain. Later in this chapter we will start more nodes and connect them to each other to make some Lightning payments. -In the next section we will also look at how to download, configure and compile c-lightning directly from the source code. This is an optional and advanced step that will teach you how to use the build tools and allow you to make modifications to c-lighting source code. With this knowledge you can write some code, fix some bugs, or create a plugin for c-lightning. If you are not planning on diving into the source code or programming of a Lightning node, you can skip the next section entirely. The Docker container we just built is sufficient for most of the examples in the book. +In the next section we will also look at how to download, configure and compile c-lightning directly from the source code. This is an optional and advanced step that will teach you how to use the build tools and allow you to make modifications to c-lighting source code. With this knowledge you can write some code, fix some bugs, or create a plugin for c-lightning. + +[NOTE] +==== +If you are not planning on diving into the source code or programming of a Lightning node, you can skip the next section entirely. The Docker container we just built is sufficient for most of the examples in the book. +==== ==== Installing c-lightning from source code @@ -455,6 +405,8 @@ https://github.com/ElementsProject/lightning/blob/master/doc/INSTALL.md ==== Installing prerequisite libraries and packages +These installation instructions assume you are building c-lightning on a Linux or similar system with GNU build tools. If that is not the case, look for the instructions for your operating system in the ElementsProject repository above. + The common first step is the installation of prerequisite libraries. We use the +apt+ package manager to install these: [source,bash] @@ -524,7 +476,6 @@ Next, we use a set of _build scripts_ that are commonly available in many open s Running the +configure+ with the +help+ option will show us all the available options: -[source,bash] ---- $ ./configure --help Usage: ./configure [--reconfigure] [setting=value] [options] @@ -618,9 +569,28 @@ https://github.com/LightningNetwork/lnd In the next few sections we will build a Docker container to run LND, build LND from source code, and learn how to configure and run LND. -==== Building LND as a Docker container +==== The LND docker container + +We can pull the LND example docker container from the book's Docker Hub repository: + +[source,bash] +---- +$ docker pull lnbook/lnd +Using default tag: latest +latest: Pulling from lnbook/lnd +35807b77a593: Already exists +e1b85b9c5571: Already exists +52f9c252546e: Pull complete + +[...] + +Digest: sha256:e490a0de5d41b781c0a7f9f548c99e67f9d728f72e50cd4632722b3ed3d85952 +Status: Downloaded newer image for lnbook/lnd:latest +docker.io/lnbook/lnd:latest + +---- -If you have followed the previous examples in this chapter, you should be quite familiar with the basic Docker commands by now. In this section we will repeat them to build the LND container. The container is located in +code/docker/lnd+. We issue commands in a terminal to change the working directory to +code/docker+ and perform the +docker build+ command: +Alternatively, we can build the LND container locally. The container is located in +code/docker/lnd+. We change the working directory to +code/docker+ and perform the +docker build+ command: [source,bash] ---- @@ -641,17 +611,18 @@ Successfully tagged lnbook/lnd:latest ---- -Our container is now built and ready to run. As with the c-lightning container we built previously, the LND container also depends on a running instance of Bitcoin Core. As before, we need to start the bitcoind container in another terminal and connect LND to it via a docker network. We have already set up a docker network called +lnbook+ previously and will be using that again here. +Our container is now ready to run. As with the c-lightning container we built previously, the LND container also depends on a running instance of Bitcoin Core. As before, we need to start the bitcoind container in another terminal and connect LND to it via a docker network. We have already set up a docker network called +lnbook+ previously and will be using that again here. [TIP] ==== -Normally, each node operator runs its own Lightning node and its own Bitcoin node on their own server. For us, a single bitcoind container can serve many Lightning nodes. On our simulated network we can run several Lightning nodes, all connecting to a single Bitcoin node in regtest mode. +Normally, each node operator runs their own Lightning node and their own Bitcoin node on their own server. For us, a single bitcoind container can serve many Lightning nodes. On our simulated network we can run several Lightning nodes, all connecting to a single Bitcoin node in regtest mode. ==== ==== Running the bitcoind and LND containers As before, we start the bitcoind container in one terminal and LND in another. If you already have the bitcoind container running, you do not need to restart it. Just leave it running and skip the next step. To start bitcoind in the +lnbook+ network we use +docker run+ like this: +[source,bash] ---- $ docker run -it --network lnbook --name bitcoind lnbook/bitcoind ---- @@ -666,31 +637,27 @@ Waiting for bitcoind to mine blocks... Starting lnd... Startup complete Funding lnd wallet -{"result":"795a8f4fce17bbab35a779e92596ba0a4a1a99aec493fa468a349c73cb055e99","error":null,"id":"lnd-run-container"} - -[...] - -2020-06-23 13:42:51.841 [INF] LTND: Active chain: Bitcoin (network=regtest) +{"result":"dbd1c8e2b224e0a511c11efb985dabd84d72d935957ac30935ec4211d28beacb","error":null,"id":"lnd-run-container"} +[INF] LTND: Version: 0.13.1-beta commit=v0.13.1-beta, build=production, logging=default, debuglevel=info +[INF] LTND: Active chain: Bitcoin (network=regtest) +[INF] RPCS: Generating TLS certificates... ---- The LND container starts up and connects to the bitcoind container over the Docker network. First, our LND node will wait for bitcoind to start and then it will wait until bitcoind has mined some bitcoin into its wallet. Finally, as part of the container startup, a script will send an RPC command to the bitcoind node thereby creating a transaction that funds the LND wallet with 10 test BTC. -As we demonstrated previously, we can issue commands to our container in another terminal in order to extract information, open channels etc. The command that allows us to issue command-line instructions to the +lnd+ daemon is called +lncli+. Let's get the node information using the +docker exec+ command in another terminal window: +As we demonstrated previously, we can issue commands to our container in another terminal in order to extract information, open channels etc. The command that allows us to issue command-line instructions to the +lnd+ daemon is called +lncli+. Once again, in this container we have provided the alias +cli+ that runs +lncli+ with all the appropriate parameters. Let's get the node information using the +docker exec+ command in another terminal window: [source,bash] ---- -$ docker exec lnd lncli -n regtest getinfo +$ docker exec lnd cli getinfo { - "version": "0.10.99-beta commit=clock/v1.0.0-85-gacc698a6995b35976950282b29c9685c993a0364", - "commit_hash": "acc698a6995b35976950282b29c9685c993a0364", - "identity_pubkey": "03e436739ec70f3c3630a62cfe3f4b6fd60ccf1f0b69a0036f73033c1ac309426e", - "alias": "03e436739ec70f3c3630", - "color": "#3399ff", - "num_pending_channels": 0, - "num_active_channels": 0, - "num_inactive_channels": 0, - [...] + "version": "0.13.1-beta commit=v0.13.1-beta", + "commit_hash": "596fd90ef310cd7abbf2251edaae9ba4d5f8a689", + "identity_pubkey": "02d4545dccbeda29a10f44e891858940f4f3374b75c0f85dcb7775bb922fdeaa14", + +[...] + } ---- @@ -698,13 +665,19 @@ We now have another Lightning node running on the +lnbook+ network and communica If desired, you can run any combination of LND and c-lightning nodes on the same Lightning Network. For example, to run a second LND node you would issue the +docker run+ command with a different container name like so: +[source,bash] ---- $ docker run -it --network lnbook --name lnd2 lnbook/lnd ---- In the command above, we start another LND container, naming it +lnd2+. The names are entirely up to you, as long as they are unique. If you don't provide a name, Docker will construct a unique name by randomly combining two English words such as "naughty_einstein". This was the actual name Docker chose for us when we wrote this paragraph. How funny! -In the next section we will look at how to download and compile LND directly from the source code. This is an optional and advanced step that will teach you how to use the Go language build tools and allow you to make modifications to LND source code. With this knowledge you can write some code or fix some bugs. If you are not planning on diving into the source code or programming of a Lightning node, you can skip the next section entirely. The Docker container we just built is sufficient for most of the examples in the book. +In the next section we will look at how to download and compile LND directly from the source code. This is an optional and advanced step that will teach you how to use the Go language build tools and allow you to make modifications to LND source code. With this knowledge you can write some code or fix some bugs. + +[NOTE] +==== +If you are not planning on diving into the source code or programming of a Lightning node, you can skip the next section entirely. The Docker container we just built is sufficient for most of the examples in the book. +==== ==== Installing LND from source code @@ -716,12 +689,14 @@ https://github.com/lightningnetwork/lnd/blob/master/docs/INSTALL.md First, we will install the +golang+ package and associated libraries. We strictly require Go version 1.13 or later. The official Go language packages are distributed as binaries from https://golang.org/dl. For convenience they are also packaged as Debian packages available through the +apt+ command. You can follow the instructions on https://golang.org/dl or use the +apt+ commands below on a Debian/Ubuntu Linux system as described on https://github.com/golang/go/wiki/Ubuntu: +[source,bash] ---- $ sudo apt install golang-go ---- Check that you have the correct version installed and ready to use by running: +[source,bash] ---- $ go version go version go1.13.4 linux/amd64 @@ -729,6 +704,7 @@ go version go1.13.4 linux/amd64 We have 1.13.4, so we're ready to... Go! Next we need to tell any programs where to find the Go code. This is accomplished by setting the environment variable +GOPATH+. Usually the Go code is located in a directory named +gocode+ directly in the user's home directory. With the following two commands we consistently set the +GOPATH+ and make sure your shell adds it to your executable +PATH+. Note that the user's home directory is referred to as +~+ in the shell. +[source,bash] ---- $ export GOPATH=~/gocode $ export PATH=$PATH:$GOPATH/bin @@ -740,6 +716,7 @@ To avoid having to set these environment variables every time you open a shell, As with many open source projects nowadays, the source code for LND is on Github (www.github.com). The +go get+ command can fetch it directly using the Git protocol: +[source,bash] ---- $ go get -d github.com/lightningnetwork/lnd ---- @@ -750,6 +727,7 @@ Once +go get+ finishes, you will have a sub-directory under +GOPATH+ that contai LND uses the +make+ build system. To build the project, we change directory to LND's source code and then use +make+ like this: +[source,bash] ---- $ cd $GOPATH/src/github.com/lightningnetwork/lnd $ make && make install @@ -776,35 +754,51 @@ https://github.com/ACINQ/eclair In the next few sections we will build a Docker container to run Eclair, as we did previously with c-lightning and LND. We will also build Eclair directly from the source code. -==== Building Eclair as a Docker container +==== The Eclair docker container -By now, you are almost an expert in the basic operations of Docker! In this section we will repeat many of the previously seen commands to build the Eclair container. The container is located in +code/docker/eclair+. We start in a terminal by switching the working directory to +code/docker+ and issuing the +docker build+ command: +Let's pull the book's eclair container from the Docker Hub repository: + +[source,bash] +---- +$ docker pull lnbook/eclair +Using default tag: latest +latest: Pulling from lnbook/eclair +35807b77a593: Already exists +e1b85b9c5571: Already exists + +[...] + +c7d5d5c616c2: Pull complete +Digest: sha256:17a3d52bce11a62381727e919771a2d5a51da9f91ce2689c7ecfb03a6f028315 +Status: Downloaded newer image for lnbook/eclair:latest +docker.io/lnbook/eclair:latest + +---- + +Alternatively, we can build the container locally, instead. By now, you are almost an expert in the basic operations of Docker! In this section we will repeat many of the previously seen commands to build the Eclair container. The container is located in +code/docker/eclair+. We start in a terminal by switching the working directory to +code/docker+ and issuing the +docker build+ command: [source,bash] ---- $ cd code/docker $ docker build -t lnbook/eclair eclair -Sending build context to Docker daemon 9.216kB -Step 1/22 : FROM ubuntu:20.04 AS eclair-base - ---> c3c304cb4f22 -Step 2/22 : RUN apt update && apt install -yqq curl gosu jq bash-completion - ---> Using cache - ---> 3f020e1a2218 -Step 3/22 : RUN apt update && apt install -yqq openjdk-11-jdk unzip - ---> Using cache - ---> b068481603f0 +Sending build context to Docker daemon 11.26kB +Step 1/27 : ARG OS=ubuntu +Step 2/27 : ARG OS_VER=focal +Step 3/27 : FROM ${OS}:${OS_VER} as os-base + ---> fb52e22af1b0 [...] -Step 22/22 : CMD ["/usr/local/bin/logtail.sh"] - ---> Using cache - ---> 5307f28ff1a0 -Successfully built 5307f28ff1a0 +Step 27/27 : CMD ["/usr/local/bin/logtail.sh"] + ---> Running in fe639120b726 +Removing intermediate container fe639120b726 + ---> e6c8fe92a87c +Successfully built e6c8fe92a87c Successfully tagged lnbook/eclair:latest ---- -Our image is now built and ready to run. The Eclair container also depends on a running instance of Bitcoin Core. As before, we need to start the bitcoind container in another terminal and connect Eclair to it via a Docker network. We have already set up a Docker network called +lnbook+ and will be reusing it here. +Our image is now ready to run. The Eclair container also depends on a running instance of Bitcoin Core. As before, we need to start the bitcoind container in another terminal and connect Eclair to it via a Docker network. We have already set up a Docker network called +lnbook+ and will be reusing it here. One notable difference between Eclair and LND or c-lightning is that Eclair doesn't contain a separate bitcoin wallet but instead relies directly on the bitcoin wallet in Bitcoin Core. Recall that using LND we "funded" its bitcoin wallet by executing a transaction to transfer bitcoin from Bitcoin Core's wallet to LND's bitcoin wallet. This step is not necessary using Eclair. When running Eclair, the Bitcoin Core wallet is used directly as the source of funds to open channels. As a result, unlike the LND or c-lightning containers, the Eclair container does not contain a script to transfer bitcoin into its wallet on startup. @@ -812,56 +806,71 @@ One notable difference between Eclair and LND or c-lightning is that Eclair does As before, we start the bitcoind container in one terminal and the Eclair container in another. If you already have the bitcoind container running, you do not need to restart it. Just leave it running and skip the next step. To start +bitcoind+ in the +lnbook+ network, we use +docker run+ like this: +[source,bash] ---- $ docker run -it --network lnbook --name bitcoind lnbook/bitcoind ---- Next, we start the Eclair container we just built. We will need to attach it to the +lnbook+ network and give it a name, just as we did with the other containers: +[source,bash] ---- $ docker run -it --network lnbook --name eclair lnbook/eclair Waiting for bitcoind to start... Waiting for bitcoind to mine blocks... Starting eclair... Eclair node started -/usr/local/bin/logtail.sh -INFO fr.acinq.eclair.Plugin$ - loading 0 plugins +INFO o.b.Secp256k1Context - secp256k1 library successfully loaded +INFO fr.acinq.eclair.Plugin - loading 0 plugins INFO a.e.slf4j.Slf4jLogger - Slf4jLogger started INFO fr.acinq.eclair.Setup - hello! -INFO fr.acinq.eclair.Setup - version=0.4 commit=69c538e +INFO fr.acinq.eclair.Setup - version=0.4.2 commit=52444b0 + [...] ---- The Eclair container starts up and connects to the bitcoind container over the Docker network. First, our Eclair node will wait for bitcoind to start and then it will wait until bitcoind has mined some bitcoin into its wallet. -As we demonstrated previously, we can issue commands to our container in another terminal in order to extract information, open channels etc. The command that allows us to issue command-line instructions to the +eclair+ daemon is called +eclair-cli+. The +eclair-cli+ command expects a password which we have set to "eclair" in this container. We pass the password +eclair+ to the +eclair-cli+ command via the +p+ flag. Using the +docker exec+ command in another terminal window we get the node info from Eclair: +As we demonstrated previously, we can issue commands to our container in another terminal in order to extract information, open channels etc. The command that allows us to issue command-line instructions to the +eclair+ daemon is called +eclair-cli+. As before, in this container we have provided a useful alias to +eclair-cli+, called simply +cli+, which offers the necessary arguments and parameters. Using the +docker exec+ command in another terminal window we get the node info from Eclair: [source,bash] ---- -$ docker exec eclair eclair-cli -p eclair getinfo +$ docker exec eclair cli getinfo { - "version": "0.4-69c538e", - "nodeId": "03ca28ed39b412626dd5efc514add8916282a1360556f8101ed3f06eea43d6561a", + "version": "0.4.2-52444b0", + "nodeId": "02fa6d5042eb8098e4d9c9d99feb7ebc9e257401ca7de829b4ce757311e0301de7", "alias": "eclair", "color": "#49daaa", - "features": "0a8a", + "features": { + +[...] + + }, "chainHash": "06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f", - "blockHeight": 123, - "publicAddresses": [] + "network": "regtest", + "blockHeight": 779, + "publicAddresses": [], + "instanceId": "01eb7a68-5db0-461b-bdd0-29010df40d73" } ---- We now have another Lightning node running on the +lnbook+ network and communicating with bitcoind. You can run any number and any combination of Lightning nodes on the same Lightning network. Any number of Eclair, LND, and c-lightning nodes can coexist. For example, to run a second Eclair node you would issue the +docker run+ command with a different container name as follows: +[source,bash] ---- $ docker run -it --network lnbook --name eclair2 lnbook/eclair ---- In the above command we start another Eclair container named +eclair2+. -In the next section we will also look at how to download and compile Eclair directly from the source code. This is an optional and advanced step that will teach you how to use the Scala and Java language build tools and allow you to make modifications to Eclair's source code. With this knowledge, you can write some code or fix some bugs. If you are not planning on diving into the source code or programming of a Lightning node, you can skip the next section entirely. The Docker container we just built is sufficient for most of the examples in the book. +In the next section we will also look at how to download and compile Eclair directly from the source code. This is an optional and advanced step that will teach you how to use the Scala and Java language build tools and allow you to make modifications to Eclair's source code. With this knowledge, you can write some code or fix some bugs. + +[NOTE] +==== +If you are not planning on diving into the source code or programming of a Lightning node, you can skip the next section entirely. The Docker container we just built is sufficient for most of the examples in the book. +==== ==== Installing Eclair from source code @@ -873,6 +882,7 @@ The required Java compiler is part of OpenJDK 11. We will also need a build fram On a Debian/Ubuntu Linux system we can use the +apt+ command to install both OpenJDK11 and Maven as shown below: +[source,bash] ---- $ sudo apt install openjdk-11-jdk maven ---- @@ -909,6 +919,7 @@ Once +git clone+ finishes you will have a sub-directory +eclair+ containing the Eclair uses the +Maven+ build system. To build the project we change the working directory to Eclair's source code and then use +mvn package+ like this: +[source,bash] ---- $ cd eclair $ mvn package @@ -916,21 +927,13 @@ $ mvn package [INFO] ------------------------------------------------------------------------ [INFO] Reactor Build Order: [INFO] -[INFO] eclair_2.13 [pom] -[INFO] eclair-core_2.13 [jar] -[INFO] eclair-node [jar] -[INFO] eclair-node-gui [jar] -[INFO] [INFO] --------------------< fr.acinq.eclair:eclair_2.13 >--------------------- [INFO] Building eclair_2.13 0.4.3-SNAPSHOT [1/4] [INFO] --------------------------------[ pom ]--------------------------------- [...] -[INFO] eclair_2.13 ........................................ SUCCESS [ 3.032 s] -[INFO] eclair-core_2.13 ................................... SUCCESS [ 7.935 s] -[INFO] eclair-node ........................................ SUCCESS [ 35.127 s] -[INFO] eclair-node-gui .................................... SUCCESS [ 20.535 s] + [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ @@ -940,8 +943,6 @@ $ mvn package ---- -The build logs above contain "2.13", for building a commit around version 0.4.3, this is expected. - After several minutes the build of the Eclair package should complete. However, the "package" action will also run tests, and some of these connect to the internet and could fail. If you want to skip tests, add +-DskipTests+ to the command. Now, unzip and run the build package by following the instructions found here: @@ -952,7 +953,7 @@ Congratulations! You have built Eclair from source and you are ready to code, te === Building a complete network of diverse Lightning nodes -Our final example, presented in this section, will bring together all the various containers we've built to form a Lightning network made of diverse (LND, c-lightning, Eclair) node implementations. We'll compose the network by connecting the nodes together and opening channels from one node to another. As the final step, we'll route a payment across these channels. +Our final example, presented in this section, will bring together all the various containers we've built to form a Lightning network made of diverse (LND, c-lightning, Eclair) node implementations. We'll compose the network by connecting the nodes together and opening channels from one node to another. As the final step, we'll route a payment across these channels! In this example, we will replicate the Lighting network example from <>. Specifically, we will create four Lightning nodes named Alice, Bob, Chan, and Dina. We will connect Alice to Bob, Bob to Chan, and Chan to Dina. Finally, we will have Dina create an invoice and have Alice pay that invoice. Since Alice and Dina are not directly connected, the payment will be routed as an HTLC across all the payment channels. @@ -966,6 +967,7 @@ https://docs.docker.com/compose/install/ Once you have completed installation, you can verify your installation by running docker-compose like this: +[source,bash] ---- $ docker-compose version docker-compose version 1.21.0, build unknown @@ -1016,27 +1018,42 @@ Because we use the same names for these orchestrated Docker containers, we might To start the example, we switch to the directory that contains the +docker-compose.yml+ configuration file and we issue the command +docker-compose up+: +[source,bash] ---- $ cd code/docker $ docker-compose up -Creating network "docker_lnnet" with the default driver -Creating Chan ... done +Creating Chan ... done +Creating Dina ... done +Creating bitcoind ... done Creating Bob ... done -Creating Dina ... done Creating Alice ... done -Creating bitcoind ... done -Attaching to Chan, Bob, Dina, Alice, bitcoind -Bob | Waiting for bitcoind to start... -Chan | Waiting for bitcoind to start... +Attaching to Chan, Dina, Alice, bitcoind, Bob Alice | Waiting for bitcoind to start... -Dina | Waiting for bitcoind to start... +Bob | Waiting for bitcoind to start... +Dina | Waiting for bitcoind to start... +Chan | Waiting for bitcoind to start... bitcoind | Starting bitcoind... +bitcoind | Waiting for bitcoind to start +bitcoind | bitcoind started +bitcoind | ================================================ + +[...] + +Chan | Starting eclair... +Dina | Starting lnd... +Chan | Eclair node started +Alice | ...Waiting for bitcoind to mine blocks... +Bob | ...Waiting for bitcoind to mine blocks... +Alice | Starting lnd... +Bob | Starting c-lightning... [...] + ---- Following the start up, you will see a whole stream of log files as each of the nodes starts up and reports its progress. It may look quite jumbled on your screen, but each output line is prefixed by the container name as seen above. If you wanted to watch the logs from only one container, you can do so in another terminal window by using the +docker-compose logs+ command with the +f+ (_follow_) flag and the specific container name: +[source,bash] ---- $ docker-compose logs -f Alice ---- @@ -1045,117 +1062,63 @@ $ docker-compose logs -f Alice Our Lightning network should now be running. As we saw in the previous sections of this chapter, we can issue commands to a running Docker container with the +docker exec+ command. Regardless of whether we started the container with +docker run+ or started a bunch of them with +docker-compose up+, we can still access containers individually using the Docker commands. -To make things easier, we have a little helper script that sets up the network, issues the invoice and makes the payment. The script is called +setup-channels.sh+ and is a Bash shell script. Keep in mind that this script is not very sophisticated! It "blindly" throws commands at the various nodes and doesn't do any error checking. If the network is running correctly and the nodes are funded, then it all works nicely. However, you have to wait a bit for everything to boot up and for the network to mine a few blocks and settle down. This usually takes 1 to 3 minutes. Once you see the block height at 102 or above on each of the nodes, then you are ready. If the script fails, you can stop everything (+docker-compose down+) and try again from the beginning. Or you can manually issue the commands found in the Bash script one by one and look at the results. - -[TIP] -==== -Before running the +setup-channels.sh+ script note the following: Wait a minute or two after starting the network with +docker-compose+ to assure that all the services are running and all the wallets are funded. To keep things simple, the script doesn't check whether the containers are "ready". Be patient! -==== +The payment demo is contained in a Bash shell script called +run-payment-demo.sh+. To run this demo you must have the Bash shell installed on your computer. Most Linux and Unix-like systems (e.g. MacOS) have +bash+ pre-installed. Windows users can install the _Windows Subsystem for Linux_ and use a Linux distribution like _Ubuntu_ to get a native +bash+ command on their computer. Let's run the script to see its effect and then we will look at how it works internally. We use +bash+ to run it as a command: -[source,bash] ---- $ cd code/docker -$ bash setup-channels.sh -Getting node IDs -Alice: 02c93da7a0a341d28e6d7742721a7d182f878e0c524e3666d80a58e1406d6d9391 -Bob: 0343b8f8d27a02d6fe688e3596b2d0834c576672e8750106540617b6d5755c812b -Chan: 03e17cbc7b46d553bade8687310ee0726e40dfa2c629b8b85ca5d888257757edc1 -Dina: 038c9dd0f0153cba3089616836936b2dad9ea7f97ef839f5fbca3a808d232db77b - -Setting up channels... -Alice to Bob - -Bob to Chan - -Chan to Dina - -Get 10k sats invoice from Dina - -Dina invoice lnbcrt100u1p00w5sypp5fw2gk98v6s4s2ldfwxa6jay0yl3f90j82kv6xx97jfwpa3s964vqdqqcqzpgsp5jpasdchlcx85hzfp9v0zc7zqs9sa3vyasj3nm0t4rsufrl7xge6s9qy9qsqpdd5d640agrhqe907ueq8n8f5h2p42vpheuzen58g5fwz94jvvnrwsgzd89v70utn4d7k6uh2kvp866zjgl6g85cxj6wtvdn89hllvgpflrnex - -Wait for channel establishment - 60 seconds for 6 blocks - ----- - -As you can see from the output, the script first gets the node IDs (public keys) for each of the four nodes. Then, it connects the nodes and sets up a 1,000,000 satoshi channel from each node to the next in the network. - -Looking inside the script, we see the part that gets all the node IDs and stores them in temporary variables so that they can be used in subsequent command. It looks like this: - -[source,bash] ----- -alice_address=$(docker-compose exec -T Alice bash -c "lncli -n regtest getinfo | jq .identity_pubkey") -bob_address=$(docker-compose exec -T Bob bash -c "lightning-cli getinfo | jq .id") -chan_address=$(docker-compose exec -T Chan bash -c "eclair-cli -s -j -p eclair getinfo| jq .nodeId") -dina_address=$(docker-compose exec -T Dina bash -c "lncli -n regtest getinfo | jq .identity_pubkey") ----- - -If you have followed the first part of the chapter, you will recognise these commands and be able to "decipher" their meaning. It looks quite complex, but we will walk through it step-by-step and you'll quickly get the hang of it. - -The first command sets up a variable called +alice_address+ that is the output of a +docker-compose exec+ command. The +T+ flag tells +docker-compose+ to not open an interactive terminal. An interactive terminal may mess up the output with things like color-coding of results. The +exec+ command is directed to the +Alice+ container and runs the +lncli+ utility since +Alice+ is an LND node. The +lncli+ command must be told that it is operating on the +regtest+ network and will then issue the +getinfo+ command to LND. The output from +getinfo+ is a JSON-encoded object, which we can parse by piping the output to the +jq+ command. The +jq+ command selects the +identity_pubkey+ field from the JSON object. The contents of the +identity_pubkey+ field are then output and stored in +alice_address+. - -The following three lines do the same for each of the other nodes. Because they are different node implementations (c-lightning, Eclair), their command-line interface is slightly different, but the general principle is the same: use the command utility to ask the node for its public key (node ID) information and parse it with +jq+, storing it in a variable for further use later. - -Next, we tell each node to establish a network connection to the next node and open a channel: - -[source,bash] ----- -docker-compose exec -T Alice lncli -n regtest connect ${bob_address//\"}@Bob -docker-compose exec -T Alice lncli -n regtest openchannel ${bob_address//\"} 1000000 ----- - -Both of the commands are directed to the +Alice+ container since the channel will be opened _from_ +Alice+ _to_ +Bob+, and +Alice+ will initiate the connection. +$ bash run-payment-demo.sh +Starting Payment Demo +====================================================== + +Waiting for nodes to startup +- Waiting for bitcoind startup... +- Waiting for bitcoind mining... +- Waiting for Alice startup... +- Waiting for Bob startup... +- Waiting for Chan startup... +- Waiting for Dina startup... +All nodes have started +====================================================== -As you can see, in the first command we tell +Alice+ to connect to the node +Bob+. Its node ID is stored in +${bob_address}+ and its IP address can be resolved from the name +Bob+, hence +@Bob+ is used as the network identifier/address. We do not need to add the port number (9375) because we are using the default Lightning ports. - -Now that +Alice+ is connected, we open a 1,000,000 satoshi channel to +Bob+ with the +openchannel+ command. Again, we refer to +Bob+'s node by the node ID, i.e. the public key. +Getting node IDs +- Alice: 0335e200756e156f1e13c3b901e5ed5a28b01a3131cd0656a27ac5cc20d4e71129 +- Bob: 033e9cb673b641d2541aaaa821c3f9214e8a11ada57451ed5a0eab2a4afbce7daa +- Chan: 02f2f12182f56c9f86b9aa7d08df89b79782210f0928cb361de5138364695c7426 +- Dina: 02d9354cec0458e0d6dee5cfa56b83040baddb4ff88ab64960e0244cc618b99bc3 +====================================================== -We do the same with the other nodes, setting up connections and channels. Each node type has a slightly different syntax for these commands, but the overall principle is the same: +[...] -To Bob's node (c-lightning) we send these commands: -[source,bash] ----- -docker-compose exec -T Bob lightning-cli connect ${chan_address//\"}@Chan -docker-compose exec -T Bob lightning-cli fundchannel ${chan_address//\"} 1000000 ----- +Setting up connections and channels +- Alice to Bob +- Open connection from Alice node to Bob's node -To Chan's node (Eclair) we send: -[source,bash] ----- -docker-compose exec -T Chan eclair-cli -p eclair connect --uri=${dina_address//\"}@Dina -docker-compose exec -T Chan eclair-cli -p eclair open --nodeId=${dina_address//\"} --fundingSatoshis=1000000 ----- +- Create payment channel Alice->Bob -At this point we create a new invoice for 10,000 satoshis on Dina's node: -[source,bash] ----- -dina_invoice=$(docker-compose exec -T Dina lncli -n regtest addinvoice 10000 | jq .payment_request) ----- - -The +addinvoice+ command creates an invoice for the specified amount in satoshis and produces a JSON object with the invoice details. From that JSON object we only need the actual bech32-encoded payment request, which we extract with +jq+. +[...] -Next, we have to wait. We just created a bunch of channels. Hence, our nodes broadcast several funding transactions. The channels can't be used until the funding transactions are mined and collect 6 confirmations. Since our Bitcoin +regtest+ blockchain is set to mine blocks every ten seconds, we have to wait 60 seconds for all the channels to be ready to use. +Get 10k sats invoice from Dina +- Dina invoice: +lnbcrt100u1psnuzzrpp5rz5dg4wy27973yr7ehwns5ldeusceqdaq0hguu8c29n4nsqkznjsdqqcqzpgxqyz5vqsp5vdpehw33fljnmmexa6ljk55544f3syd8nfttqlm3ljewu4r0q20q9qyyssqxh5nhkpjgfm47yxn4p9ecvndz7zddlsgpufnpyjl0kmnq227tdujlm0acdv39hcuqp2vhs40aav70c9yp0tee6tgzk8ut79mr877q0cpkjcfvr +====================================================== -The final command is the actual invoice payment. We connect to Alice's node and present Dina's invoice for payment. +Attempting payment from Alice to Dina +Successful payment! -[source,bash] ----- -docker-compose exec -T Alice lncli -n regtest payinvoice --json --inflight_updates -f ${dina_invoice//\"} ---- -We ask Alice's node to pay the invoice, but also ask for +inflight_updates+ in +json+ format. That will give us detailed output about the invoice, the route, the HTLCs, and the final payment result. We can study this additional output and learn from it! -Since Alice's node doesn't have a direct channel to Dina, her node has to find a route. There is only one viable route here (Alice->Bob->Chan->Dina), which Alice will be able to discover now that all the channels are active and have been advertised to all the nodes by the Lightning gossip protocol. Alice's node will construct the route and create an onion packet to establish HTLCs across the channels. All of this happens in a fraction of a second and Alice's node will report the result of the payment attempt. If all goes well, you will see the last line of the JSON output showing: +As you can see from the output, the script first gets the node IDs (public keys) for each of the four nodes. Then, it connects the nodes and sets up a 1,000,000 satoshi channel from each node to the next in the network. Finally, it issues an invoice for 10k satoshis from Dina's node and pays the invoice from Alice's node. ----- -"failure_reason": "FAILURE_REASON_NONE" ----- - -This is arguably a weird message, but the fact that there was no failure reason, in a round-about way, implies that the operation was a success! +[TIP] +==== +If the script fails, you can try running it again from the beginning. Or you can manually issue the commands found in the script one by one and look at the results. +==== -Scrolling above that unusual message you will see all the details of the payment. There is a lot to review, but as you gain understanding of the underlying technology, more and more of that information will become clear. You are invited to revisit this example later. +There is a lot to review in that script, but as you gain understanding of the underlying technology, more and more of that information will become clear. You are invited to revisit this example later. Of course, you can do a lot more with this test network than a 3-channel, 4-node payment. Here are some ideas for your experiments: diff --git a/appendix_docker_basics.asciidoc b/appendix_docker_basics.asciidoc new file mode 100644 index 0000000..3a0c121 --- /dev/null +++ b/appendix_docker_basics.asciidoc @@ -0,0 +1,113 @@ +[appendix] +[[appendix_docker]] +== Docker Basic Installation and Use + +This book contains a number of examples that run inside docker containers, for standardization across different operating systems. + +This section will help you install Docker and familiarize yourself with some of the most commonly used Docker commands, so that you can run the book's example containers. + + +=== Installing Docker + +Before we begin, you should install the Docker container system on your computer. Docker is an open system that is distributed for free as a _Community Edition_ for many different operating systems including Windows, Mac OS and Linux. The Windows and Mac versions are called _Docker Desktop_ and consist of a GUI desktop application and command-line tools. The Linux version is called _Docker Engine_ and is comprised of a server daemon and command-line tools. We will be using the command-line tools, which are identical across all platforms. + +Go ahead and install Docker for your operating system by following the instructions to _"Get Docker"_ from the Docker website found here: + +https://docs.docker.com/get-docker/ + +Select your operating system from the list and follow the installation instructions. + +[TIP] +==== +If you install on Linux, follow the post-installation instructions to ensure you can run Docker as a regular user instead of user _root_. Otherwise, you will need to prefix all +docker+ commands with +sudo+, running them as root like: +sudo docker+. +==== + +Once you have Docker installed, you can test your installation by running the demo container +hello-world+ like this: + +[docker-hello-world] +---- +$ docker run hello-world + +Hello from Docker! +This message shows that your installation appears to be working correctly. + +[...] +---- + +=== Basic Docker commands + +In this chapter, we use Docker quite extensively. We will be using the following Docker commands and arguments: + +*Building a container* + +---- +docker build [-t tag] [directory] +---- + +...where +tag+ is how we identify the container we are building, and +directory+ is where the container's "context" (folders and files) and definition file (+Dockerfile+) are found. + +*Running a container* + +---- +docker run -it [--network netname] [--name cname] tag +---- + +...where +netname+ is the name of a Docker network, +cname+ is the name we choose for this container instance and +tag+ is the name tag we gave the container when we built it. + +*Executing a command in a container* + +---- +docker exec cname command +---- + +...where +cname+ is the name we gave the container in the +run+ command, and +command+ is an executable or script that we want to run inside the container. + +*Stopping and starting a container* + +In most cases, if we are running a container in an _interactive_ as well as _terminal_ mode, i.e. with the +i+ and +t+ flags (combined as +-it+) set, the container can be stopped by simply pressing +CTRL-C+ or by exiting the shell with +exit+ or +CTRL-D+. If a container does not terminate, you can stop it from another terminal like this: + +---- +docker stop cname +---- + +To resume an already existing container use the `start` command, like so: + +---- +docker start cname +---- + +*Deleting a container by name* + +If you name a container instead of letting Docker name it randomly, you cannot reuse that name until the container is deleted. Docker will return an error like this: +[source,bash] +---- +docker: Error response from daemon: Conflict. The container name "/bitcoind" is already in use... +---- + +To fix this, delete the existing instance of the container: + +---- +docker rm cname +---- + +...where +cname+ is the name assigned to the container (+bitcoind+ in the example error message) + +*List running containers* + +---- +docker ps +---- + +...shows the current running containers and their names + +*List docker images* + +---- +docker image ls +---- + +...shows the docker images that have been built or downloaded on your computer + +=== Conclusion + +These basic Docker commands will be enough to get you started and will allow you to run all the examples in this book. diff --git a/code/docker/Makefile b/code/docker/Makefile index d62a875..84ec989 100644 --- a/code/docker/Makefile +++ b/code/docker/Makefile @@ -1,16 +1,126 @@ +#!make +# +# Makefile to help with building, pulling and pushing containers +# +# NOTE: You cannot push to the container registry unless you are authorized +# in the lnbook organization (i.e. one of the authors or maintainers) +# +# Targets: +# +# make build # Build all containers +# make pull # Pull all containers from the registry +# make build-bitcoind # Build a specific container +# make clean # remove all images and containers +# make push # push updated images to Docker Hub (authors/maintainers only) + +# Latest tested versions of Bitcoin and Lightning clients + +# OS base image +OS=ubuntu +OS_VER=focal + +# bitcoind version +BITCOIND_VER=0.21.0 + +# LND version +GO_VER=1.13 +LND_VER=v0.13.1-beta + +# c-lightning version +CL_VER=0.10.1 + +# Eclair version +ECLAIR_VER=0.4.2 +ECLAIR_COMMIT=52444b0 + + + + +# Docker registry for lnbook REGISTRY=docker.com -NAME=lnbook +ORG=lnbook + +# List of containers CONTAINERS=bitcoind lnd eclair c-lightning -all: build-all push-all +.DEFAULT: pull + + + -build-all: - for container in ${CONTAINERS}; do \ - docker build -t ${NAME}/$$container $$container -f $$container/Dockerfile; \ - done -push-all: +build-bitcoind: + docker build \ + --build-arg OS=${OS} \ + --build-arg OS_VER=${OS_VER} \ + --build-arg BITCOIND_VER=${BITCOIND_VER} \ + -t ${ORG}/bitcoind:${BITCOIND_VER} \ + bitcoind -f bitcoind/Dockerfile + docker image tag ${ORG}/bitcoind:${BITCOIND_VER} ${ORG}/bitcoind:latest + + +build-cl: build-bitcoind + docker build \ + --build-arg OS=${OS} \ + --build-arg OS_VER=${OS_VER} \ + --build-arg CL_VER=${CL_VER} \ + -t ${ORG}/c-lightning:${CL_VER} \ + c-lightning -f c-lightning/Dockerfile + docker image tag ${ORG}/c-lightning:${CL_VER} ${ORG}/c-lightning:latest + + +build-lnd: + docker build \ + --build-arg OS=${OS} \ + --build-arg OS_VER=${OS_VER} \ + --build-arg LND_VER=${LND_VER} \ + --build-arg GO_VER=${GO_VER} \ + -t ${ORG}/lnd:${LND_VER}_golang_${GO_VER} \ + lnd -f lnd/Dockerfile + docker image tag ${ORG}/lnd:${LND_VER}_golang_${GO_VER} ${ORG}/lnd:latest + + +build-eclair: + docker build \ + --build-arg OS=${OS} \ + --build-arg OS_VER=${OS_VER} \ + --build-arg ECLAIR_VER=${ECLAIR_VER} \ + --build-arg ECLAIR_COMMIT=${ECLAIR_COMMIT} \ + -t ${ORG}/eclair:${ECLAIR_VER}-${ECLAIR_COMMIT} \ + eclair -f eclair/Dockerfile + docker image tag ${ORG}/eclair:${ECLAIR_VER}-${ECLAIR_COMMIT} ${ORG}/eclair:latest + + +push-bitcoind: build-bitcoind + docker push ${ORG}/bitcoind:${BITCOIND_VER} + docker push ${ORG}/bitcoind:latest + +push-lnd: build-lnd + docker push ${ORG}/lnd:${LND_VER}_golang_${GO_VER} + docker push ${ORG}/lnd:latest + +push-cl: build-cl + docker push ${ORG}/c-lightning:${CL_VER} + docker push ${ORG}/c-lightning:latest + +push-eclair: build-eclair + docker push ${ORG}/eclair:${ECLAIR_VER}-${ECLAIR_COMMIT} + docker push ${ORG}/eclair:latest + +build: build-bitcoind build-lnd build-cl build-eclair + +push: push-bitcoind push-lnd push-cl push-eclair + +pull: for container in ${CONTAINERS}; do \ - docker push ${NAME}/$$container; \ + docker pull ${ORG}/$$container:latest ;\ done + +clean: + # Try 'make clean-confirm' if you are sure you want to do this. + # CAUTION: ALL docker containers and images on your computer will be removed. + +clean-confirm: + docker rm -f `docker ps -qa` + docker rmi -f `docker image ls -qa` diff --git a/code/docker/bitcoind/Dockerfile b/code/docker/bitcoind/Dockerfile index b2555b6..95375fa 100644 --- a/code/docker/bitcoind/Dockerfile +++ b/code/docker/bitcoind/Dockerfile @@ -1,34 +1,47 @@ -FROM ubuntu:20.04 AS bitcoind-base - -RUN apt update && apt install -yqq \ - curl gosu jq bash-completion - -ENV BITCOIND_VERSION 0.21.0 -# Install binaries for Bitcoin Core -ADD https://bitcoincore.org/bin/bitcoin-core-${BITCOIND_VERSION}/bitcoin-${BITCOIND_VERSION}-x86_64-linux-gnu.tar.gz /usr/local -RUN cd /usr/local/ \ - && tar -zxf bitcoin-${BITCOIND_VERSION}-x86_64-linux-gnu.tar.gz \ - && cd bitcoin-${BITCOIND_VERSION} \ - && install bin/* /usr/local/bin \ - && install include/* /usr/local/include \ - && install -v lib/* /usr/local/lib - +ARG OS=ubuntu +ARG OS_VER=focal +FROM ${OS}:${OS_VER} as os-base + +# Install dependencies +RUN DEBIAN_FRONTEND=noninteractive \ + apt-get update -qq && apt-get install -yqq \ + curl unzip jq bash-completion + +FROM os-base as bitcoind-install + +ARG BITCOIND_VER=0.21.0 +# Install Bitcoin Core binaries and libraries +RUN cd /tmp && \ + curl -# -sLO https://bitcoincore.org/bin/bitcoin-core-${BITCOIND_VER}/bitcoin-${BITCOIND_VER}-x86_64-linux-gnu.tar.gz && \ + tar -zxf bitcoin-${BITCOIND_VER}-x86_64-linux-gnu.tar.gz && \ + cd bitcoin-${BITCOIND_VER} && \ + install -vD bin/* /usr/bin && \ + install -vD lib/* /usr/lib && \ + cd /tmp && \ + rm bitcoin-${BITCOIND_VER}-x86_64-linux-gnu.tar.gz && \ + rm -rf bitcoin-${BITCOIND_VER} + +# Install runtime scripts, bash-completion and configuration files + +# bash completion for bitcoind and bitcoin-cli ENV GH_URL https://raw.githubusercontent.com/bitcoin/bitcoin/master/ ENV BC /usr/share/bash-completion/completions/ ADD $GH_URL/contrib/bitcoin-cli.bash-completion $BC/bitcoin-cli ADD $GH_URL/contrib/bitcoind.bash-completion $BC/bitcoind ADD $GH_URL/contrib/bitcoin-tx.bash-completion $BC/bitcoin-tx -FROM bitcoind-base AS bitcoind - -ADD bitcoind /bitcoind +# Copy bitcoind configuration directory +COPY bitcoind /bitcoind RUN ln -s /bitcoind /root/. -ADD bashrc /root/.bashrc -ADD bitcoind-entrypoint.sh /usr/local/bin +# Copy support scripts +COPY bashrc /root/.bashrc +COPY bitcoind-entrypoint.sh /usr/local/bin RUN chmod +x /usr/local/bin/bitcoind-entrypoint.sh -ADD mine.sh /usr/local/bin +COPY mine.sh /usr/local/bin RUN chmod +x /usr/local/bin/mine.sh +COPY cli /usr/local/bin +RUN chmod +x /usr/local/bin/cli # bitcoind P2P EXPOSE 18444/tcp diff --git a/code/docker/bitcoind/bitcoind-entrypoint.sh b/code/docker/bitcoind/bitcoind-entrypoint.sh index 51b65d7..9942b1a 100644 --- a/code/docker/bitcoind/bitcoind-entrypoint.sh +++ b/code/docker/bitcoind/bitcoind-entrypoint.sh @@ -1,25 +1,36 @@ #!/bin/bash set -Eeuo pipefail -echo Starting bitcoind... + +# Start bitcoind +echo "Starting bitcoind..." bitcoind -datadir=/bitcoind -daemon + +# Wait for bitcoind startup +echo -n "Waiting for bitcoind to start" until bitcoin-cli -datadir=/bitcoind -rpcwait getblockchaininfo > /dev/null 2>&1 do + echo -n "." sleep 1 done -echo bitcoind started +echo +echo "bitcoind started" + + +# Load private key into wallet export address=`cat /bitcoind/keys/demo_address.txt` export privkey=`cat /bitcoind/keys/demo_privkey.txt` + +# If restarting the wallet already exists, so don't fail if it does, +# just load the existing wallet: +bitcoin-cli -datadir=/bitcoind createwallet regtest > /dev/null || bitcoin-cli -datadir=/bitcoind loadwallet regtest > /dev/null +bitcoin-cli -datadir=/bitcoind importprivkey $privkey > /dev/null || true + echo "================================================" -echo "Importing demo private key" +echo "Imported demo private key" echo "Bitcoin address: " ${address} echo "Private key: " ${privkey} echo "================================================" -# If restarting the wallet already exists, so don't fail if it does, -# just load the existing wallet: -bitcoin-cli -datadir=/bitcoind createwallet regtest || bitcoin-cli -datadir=/bitcoind loadwallet regtest -bitcoin-cli -datadir=/bitcoind importprivkey $privkey || true # Executing CMD -echo "$@" exec "$@" diff --git a/code/docker/bitcoind/cli b/code/docker/bitcoind/cli new file mode 100644 index 0000000..0ad91ec --- /dev/null +++ b/code/docker/bitcoind/cli @@ -0,0 +1,5 @@ +#!/bin/bash +# +# Helper script used as an alias for bitcoin-cli with the necessary arguments +# +/usr/bin/bitcoin-cli -datadir=/bitcoind -regtest $@ diff --git a/code/docker/bitcoind/mine.sh b/code/docker/bitcoind/mine.sh index 5e08f57..c4b32b4 100755 --- a/code/docker/bitcoind/mine.sh +++ b/code/docker/bitcoind/mine.sh @@ -2,18 +2,17 @@ set -Eeuo pipefail export address=`cat /bitcoind/keys/demo_address.txt` -export privkey=`cat /bitcoind/keys/demo_privkey.txt` echo "================================================" -echo "Bitcoin address: " ${address} -echo "Private key: " ${privkey} echo "Balance:" `bitcoin-cli -datadir=/bitcoind getbalance` echo "================================================" echo "Mining 101 blocks to unlock some bitcoin" bitcoin-cli -datadir=/bitcoind generatetoaddress 101 $address -echo "Mining 1 block every 10 seconds" -while sleep 10; do \ - bitcoin-cli -datadir=/bitcoind generatetoaddress 1 $address; \ - echo "Balance:" `bitcoin-cli -datadir=/bitcoind getbalance`; \ +echo "Mining 6 blocks every 10 seconds" +while echo "Balance:" `bitcoin-cli -datadir=/bitcoind getbalance`; +do + bitcoin-cli -datadir=/bitcoind generatetoaddress 6 $address; \ + sleep 10; \ + done # If loop is interrupted, stop bitcoind diff --git a/code/docker/c-lightning/Dockerfile b/code/docker/c-lightning/Dockerfile index a0e9a49..d25caf9 100644 --- a/code/docker/c-lightning/Dockerfile +++ b/code/docker/c-lightning/Dockerfile @@ -1,14 +1,26 @@ -FROM lnbook/bitcoind AS c-lightning-base +ARG OS=ubuntu +ARG OS_VER=focal +FROM ${OS}:${OS_VER} as os-base -# Install software-properties-common to add apt repositories -RUN apt-get update -qq && apt-get install -yqq \ - wget gpg xz-utils libpq5 libsodium23 +# Install dependencies +RUN DEBIAN_FRONTEND=noninteractive \ + apt-get update -qq && apt-get install -yqq \ + curl unzip jq bash-completion + +FROM os-base as cl-install +COPY --from=lnbook/bitcoind:latest /usr/bin/bitcoin-cli /usr/bin + +# Set CL_VER ENV from ARG +ARG CL_VER=0.10.1 +ENV CL_VER=${CL_VER} -# c-lightning -ENV C_LIGHTNING_VER 0.10.1 +RUN apt-get update -qq && apt-get install -yqq \ + gpg xz-utils libpq5 libsodium23 && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* RUN cd /tmp && \ - wget -q https://github.com/ElementsProject/lightning/releases/download/v${C_LIGHTNING_VER}/clightning-v${C_LIGHTNING_VER}-Ubuntu-20.04.tar.xz + curl -# -sLO https://github.com/ElementsProject/lightning/releases/download/v${CL_VER}/clightning-v${CL_VER}-Ubuntu-20.04.tar.xz # Verify developer signatures. The `gpg --verify` command will print a # couple of warnings about the key not being trusted. That's ok. The @@ -23,25 +35,26 @@ RUN cd /tmp && \ cat SHA256SUMS && \ sha256sum --ignore-missing -c SHA256SUMS -RUN tar -xvf /tmp/clightning-v${C_LIGHTNING_VER}-Ubuntu-20.04.tar.xz -C / +RUN tar -xvf /tmp/clightning-v${CL_VER}-Ubuntu-20.04.tar.xz -C / ADD https://raw.githubusercontent.com/ElementsProject/lightning/master/contrib/lightning-cli.bash-completion /usr/share/bash-completion/completions/lightning-cli -FROM c-lightning-base AS c-lightning-run - -ADD lightningd /lightningd +COPY lightningd /lightningd WORKDIR /lightningd RUN ln -s /lightningd /root/.lightning -ADD bashrc /root/.bashrc -ADD c-lightning-entrypoint.sh /usr/local/bin +COPY bashrc /root/.bashrc +COPY c-lightning-entrypoint.sh /usr/local/bin RUN chmod +x /usr/local/bin/c-lightning-entrypoint.sh -ADD fund-c-lightning.sh /usr/local/bin +COPY fund-c-lightning.sh /usr/local/bin RUN chmod +x /usr/local/bin/fund-c-lightning.sh -ADD logtail.sh /usr/local/bin +COPY logtail.sh /usr/local/bin RUN chmod +x /usr/local/bin/logtail.sh -ADD wait-for-bitcoind.sh /usr/local/bin +COPY wait-for-bitcoind.sh /usr/local/bin RUN chmod +x /usr/local/bin/wait-for-bitcoind.sh +COPY cli /usr/local/bin +RUN chmod +x /usr/local/bin/cli + EXPOSE 9735 9835 ENTRYPOINT ["/usr/local/bin/c-lightning-entrypoint.sh"] diff --git a/code/docker/c-lightning/c-lightning-entrypoint.sh b/code/docker/c-lightning/c-lightning-entrypoint.sh index b901332..080b49f 100644 --- a/code/docker/c-lightning/c-lightning-entrypoint.sh +++ b/code/docker/c-lightning/c-lightning-entrypoint.sh @@ -15,5 +15,4 @@ sleep 2 echo "Funding c-lightning wallet" source /usr/local/bin/fund-c-lightning.sh -echo "$@" exec "$@" diff --git a/code/docker/c-lightning/cli b/code/docker/c-lightning/cli new file mode 100644 index 0000000..5071fbd --- /dev/null +++ b/code/docker/c-lightning/cli @@ -0,0 +1,5 @@ +#!/bin/bash +# +# Helper script used as an alias for lightning-cli with the necessary arguments +# +/usr/bin/lightning-cli --lightning-dir=/lightningd $@ diff --git a/code/docker/c-lightning/fund-c-lightning.sh b/code/docker/c-lightning/fund-c-lightning.sh index cac2f22..a5b772e 100644 --- a/code/docker/c-lightning/fund-c-lightning.sh +++ b/code/docker/c-lightning/fund-c-lightning.sh @@ -5,9 +5,13 @@ set -Eeuo pipefail address=$(lightning-cli --lightning-dir=/lightningd --network regtest newaddr | jq '.bech32' -r) # Ask Bitcoin Core to send 10 BTC to the address, using JSON-RPC call -bitcoin-cli \ +until bitcoin-cli \ --rpcuser=regtest \ --rpcpassword=regtest \ - --rpcconnect=bitcoind \ + --rpcconnect=bitcoind:18443 \ --regtest \ sendtoaddress ${address} 10 "funding c-lightning" +do + sleep 1; + echo Retrying funding... +done diff --git a/code/docker/c-lightning/logtail.sh b/code/docker/c-lightning/logtail.sh index d8eba20..e91c6c6 100644 --- a/code/docker/c-lightning/logtail.sh +++ b/code/docker/c-lightning/logtail.sh @@ -2,4 +2,5 @@ set -Eeuo pipefail # Show LND log from beginning and follow +touch /lightningd/lightningd.log tail -n +1 -f /lightningd/lightningd.log || true diff --git a/code/docker/c-lightning/wait-for-bitcoind.sh b/code/docker/c-lightning/wait-for-bitcoind.sh index bda3a80..44bee14 100644 --- a/code/docker/c-lightning/wait-for-bitcoind.sh +++ b/code/docker/c-lightning/wait-for-bitcoind.sh @@ -2,14 +2,14 @@ set -Eeuo pipefail echo Waiting for bitcoind to start... -until bitcoin-cli -rpcconnect=bitcoind -rpcport=18443 -rpcuser=regtest -rpcpassword=regtest getblockchaininfo > /dev/null 2>&1 +until curl --silent --user regtest:regtest --data-binary '{"jsonrpc": "1.0", "id": "cl-node", "method": "getblockchaininfo", "params": []}' -H 'content-type: text/plain;' http://bitcoind:18443/ | jq -e ".result.blocks > 0" > /dev/null 2>&1 do echo -n "." sleep 1 done echo Waiting for bitcoind to mine blocks... -until bitcoin-cli -rpcconnect=bitcoind -rpcport=18443 -rpcuser=regtest -rpcpassword=regtest getbalance | jq -e ". > 0" > /dev/null 2>&1 +until curl --silent --user regtest:regtest --data-binary '{"jsonrpc": "1.0", "id": "cl-node", "method": "getbalance", "params": ["*", 6]}' -H 'content-type: text/plain;' http://bitcoind:18443/ | jq -e ".result > 0" > /dev/null 2>&1 do echo -n "." sleep 1 diff --git a/code/docker/check-versions.sh b/code/docker/check-versions.sh index d5fd5cc..7bc7625 100644 --- a/code/docker/check-versions.sh +++ b/code/docker/check-versions.sh @@ -1,8 +1,7 @@ #!/bin/bash - # a small script to help sanity check the versions of the different node implementations dockerfiles=$(find . -name 'Dockerfile') # print location of dockerfiles echo $dockerfiles # print variables -awk '/ENV/ && /VER|COMMIT/' $dockerfiles +awk '/ARG/ && /VER|COMMIT/' $dockerfiles diff --git a/code/docker/eclair/Dockerfile b/code/docker/eclair/Dockerfile index c7f6c47..b0c7b21 100644 --- a/code/docker/eclair/Dockerfile +++ b/code/docker/eclair/Dockerfile @@ -1,20 +1,29 @@ -FROM ubuntu:20.04 AS eclair-base - -RUN apt update && apt install -yqq \ - curl gosu jq bash-completion - -RUN apt update && apt install -yqq \ - openjdk-11-jdk unzip - -COPY --from=lnbook/bitcoind /usr/local/ /usr/local/ +ARG OS=ubuntu +ARG OS_VER=focal +FROM ${OS}:${OS_VER} as os-base + +# Install dependencies +RUN DEBIAN_FRONTEND=noninteractive \ + apt-get update -qq && apt-get install -yqq \ + curl unzip jq bash-completion + +# Install default Java Runtime Environment +RUN DEBIAN_FRONTEND=noninteractive \ + apt-get update -qq && apt-get install -yqq \ + default-jre-headless && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* # Install eclair -ENV ECLAIR_VER 0.4.2 -ENV ECLAIR_COMMIT 52444b0 WORKDIR /usr/src -RUN curl -sLO https://github.com/ACINQ/eclair/releases/download/v${ECLAIR_VER}/eclair-node-${ECLAIR_VER}-${ECLAIR_COMMIT}-bin.zip \ - && unzip eclair-node-${ECLAIR_VER}-${ECLAIR_COMMIT}-bin.zip \ - && install eclair-node-${ECLAIR_VER}-${ECLAIR_COMMIT}/bin/eclair-cli /usr/local/bin + +ARG ECLAIR_VER=0.4.2 +ARG ECLAIR_COMMIT=52444b0 +RUN cd /usr/src && \ + curl -# -sLO https://github.com/ACINQ/eclair/releases/download/v${ECLAIR_VER}/eclair-node-${ECLAIR_VER}-${ECLAIR_COMMIT}-bin.zip && \ + unzip eclair-node-${ECLAIR_VER}-${ECLAIR_COMMIT}-bin.zip && \ + install eclair-node-${ECLAIR_VER}-${ECLAIR_COMMIT}/bin/eclair-cli /usr/local/bin && \ + rm eclair-node-${ECLAIR_VER}-${ECLAIR_COMMIT}-bin.zip ADD https://raw.githubusercontent.com/ACINQ/eclair/master/contrib/eclair-cli.bash-completion /usr/share/bash-completion/completions/eclair-cli @@ -29,6 +38,11 @@ ADD logtail.sh /usr/local/bin RUN chmod +x /usr/local/bin/logtail.sh ADD wait-for-bitcoind.sh /usr/local/bin RUN chmod +x /usr/local/bin/wait-for-bitcoind.sh +COPY cli /usr/local/bin +RUN chmod +x /usr/local/bin/cli + +ENV ECLAIR_VER=$ECLAIR_VER +ENV ECLAIR_COMMIT=$ECLAIR_COMMIT EXPOSE 9735 ENTRYPOINT ["/usr/local/bin/eclair-entrypoint.sh"] diff --git a/code/docker/eclair/cli b/code/docker/eclair/cli new file mode 100644 index 0000000..dcfedaf --- /dev/null +++ b/code/docker/eclair/cli @@ -0,0 +1,5 @@ +#!/bin/bash +# +# Helper script used as an alias for eclair-cli with the necessary arguments +# +/usr/local/bin/eclair-cli -s -j -p eclair $@ diff --git a/code/docker/eclair/eclair-entrypoint.sh b/code/docker/eclair/eclair-entrypoint.sh index d6110d8..f2b0b92 100644 --- a/code/docker/eclair/eclair-entrypoint.sh +++ b/code/docker/eclair/eclair-entrypoint.sh @@ -18,5 +18,4 @@ echo Eclair node started sleep 2 # Executing CMD -echo "$@" exec "$@" diff --git a/code/docker/eclair/eclair/eclair.conf b/code/docker/eclair/eclair/eclair.conf index 861118d..d286241 100644 --- a/code/docker/eclair/eclair/eclair.conf +++ b/code/docker/eclair/eclair/eclair.conf @@ -23,11 +23,18 @@ eclair { zmqblock = "tcp://bitcoind:12005" zmqtx = "tcp://bitcoind:12006" } - + on-chain-fees { feerate-tolerance { ratio-low = 0.000001 ratio-high = 1000000 } } + + node-alias = "eclair" + + router { + channel-exclude-duration = 1 seconds + broadcast-interval = 1 seconds + } } diff --git a/code/docker/eclair/wait-for-bitcoind.sh b/code/docker/eclair/wait-for-bitcoind.sh index bda3a80..16d8696 100644 --- a/code/docker/eclair/wait-for-bitcoind.sh +++ b/code/docker/eclair/wait-for-bitcoind.sh @@ -2,14 +2,14 @@ set -Eeuo pipefail echo Waiting for bitcoind to start... -until bitcoin-cli -rpcconnect=bitcoind -rpcport=18443 -rpcuser=regtest -rpcpassword=regtest getblockchaininfo > /dev/null 2>&1 +until curl --silent --user regtest:regtest --data-binary '{"jsonrpc": "1.0", "id": "eclair-node", "method": "getblockchaininfo", "params": []}' -H 'content-type: text/plain;' http://bitcoind:18443/ | jq -e ".result.blocks > 0" > /dev/null 2>&1 do echo -n "." sleep 1 done echo Waiting for bitcoind to mine blocks... -until bitcoin-cli -rpcconnect=bitcoind -rpcport=18443 -rpcuser=regtest -rpcpassword=regtest getbalance | jq -e ". > 0" > /dev/null 2>&1 +until curl --silent --user regtest:regtest --data-binary '{"jsonrpc": "1.0", "id": "eclair-node", "method": "getbalance", "params": ["*", 6]}' -H 'content-type: text/plain;' http://bitcoind:18443/ | jq -e ".result > 0" > /dev/null 2>&1 do echo -n "." sleep 1 diff --git a/code/docker/lnd/Dockerfile b/code/docker/lnd/Dockerfile index 3bad517..ba47671 100644 --- a/code/docker/lnd/Dockerfile +++ b/code/docker/lnd/Dockerfile @@ -1,40 +1,59 @@ -FROM golang:1.13 as lnd-base +ARG OS=ubuntu +ARG OS_VER=focal +ARG GO_VER=1.13 +# Define base images with ARG versions +FROM ${OS}:${OS_VER} as os +FROM golang:${GO_VER} as go -ENV GOPATH /go -WORKDIR $GOPATH/src +# OS image with command-line utilities +FROM os AS os-base + +# Install dependencies +RUN DEBIAN_FRONTEND=noninteractive \ + apt-get update -qq && apt-get install -yqq \ + curl unzip jq bash-completion + +# Go image for building LND +FROM go as lnd-build -# LND -ENV LND_VER v0.11.1-beta -RUN go get -d github.com/lightningnetwork/lnd -WORKDIR $GOPATH/src/github.com/lightningnetwork/lnd -RUN git checkout tags/${LND_VER} -RUN make && make install +ENV GO_VER=${GO_VER} +ENV GOPATH=/go -FROM ubuntu:20.04 AS lnd-run +# Build LND +ARG LND_VER=v0.13.1-beta +ENV LND_VER=${LND_VER} +RUN mkdir -p ${GOPATH}/src && \ + cd ${GOPATH}/src && \ + go get -v -d github.com/lightningnetwork/lnd && \ + cd ${GOPATH}/src/github.com/lightningnetwork/lnd && \ + git checkout tags/${LND_VER} && \ + make clean && make && make install -RUN apt update && apt install -yqq \ - curl gosu jq bash-completion +# Runtime image for running LND +FROM os-base as lnd-run -COPY --from=lnd-base /go /go -COPY --from=lnbook/bitcoind /usr/local/ /usr/local/ +# Copy only the executables +COPY --from=lnd-build /go/bin /go/bin -RUN cp /go/src/github.com/lightningnetwork/lnd/contrib/lncli.bash-completion \ +ADD https://raw.githubusercontent.com/lightningnetwork/lnd/master/contrib/lncli.bash-completion \ /usr/share/bash-completion/completions/lncli ENV GOPATH /go ENV PATH $PATH:$GOPATH/bin -ADD lnd /lnd +COPY lnd /lnd RUN ln -s /lnd /root/.lnd -ADD fund-lnd.sh /usr/local/bin +COPY fund-lnd.sh /usr/local/bin RUN chmod +x /usr/local/bin/fund-lnd.sh -ADD bashrc /root/.bashrc -ADD lnd-entrypoint.sh /usr/local/bin +COPY bashrc /root/.bashrc +COPY lnd-entrypoint.sh /usr/local/bin RUN chmod +x /usr/local/bin/lnd-entrypoint.sh -ADD logtail.sh /usr/local/bin +COPY logtail.sh /usr/local/bin RUN chmod +x /usr/local/bin/logtail.sh -ADD wait-for-bitcoind.sh /usr/local/bin +COPY wait-for-bitcoind.sh /usr/local/bin RUN chmod +x /usr/local/bin/wait-for-bitcoind.sh +COPY cli /usr/local/bin +RUN chmod +x /usr/local/bin/cli # LND RPC EXPOSE 10009/tcp diff --git a/code/docker/lnd/cli b/code/docker/lnd/cli new file mode 100644 index 0000000..d5cead8 --- /dev/null +++ b/code/docker/lnd/cli @@ -0,0 +1,5 @@ +#!/bin/bash +# +# Helper script used as an alias for lncli with the necessary arguments +# +/go/bin/lncli --lnddir=/lnd -n regtest $@ diff --git a/code/docker/lnd/lnd-entrypoint.sh b/code/docker/lnd/lnd-entrypoint.sh index 732d15b..bf13acb 100644 --- a/code/docker/lnd/lnd-entrypoint.sh +++ b/code/docker/lnd/lnd-entrypoint.sh @@ -14,5 +14,4 @@ echo "Startup complete" echo "Funding lnd wallet" source /usr/local/bin/fund-lnd.sh -echo "$@" exec "$@" diff --git a/code/docker/lnd/wait-for-bitcoind.sh b/code/docker/lnd/wait-for-bitcoind.sh index bda3a80..9767d3b 100644 --- a/code/docker/lnd/wait-for-bitcoind.sh +++ b/code/docker/lnd/wait-for-bitcoind.sh @@ -2,14 +2,14 @@ set -Eeuo pipefail echo Waiting for bitcoind to start... -until bitcoin-cli -rpcconnect=bitcoind -rpcport=18443 -rpcuser=regtest -rpcpassword=regtest getblockchaininfo > /dev/null 2>&1 +until curl --silent --user regtest:regtest --data-binary '{"jsonrpc": "1.0", "id": "lnd-node", "method": "getblockchaininfo", "params": []}' -H 'content-type: text/plain;' http://bitcoind:18443/ | jq -e ".result.blocks > 0" > /dev/null 2>&1 do echo -n "." sleep 1 done echo Waiting for bitcoind to mine blocks... -until bitcoin-cli -rpcconnect=bitcoind -rpcport=18443 -rpcuser=regtest -rpcpassword=regtest getbalance | jq -e ". > 0" > /dev/null 2>&1 +until curl --silent --user regtest:regtest --data-binary '{"jsonrpc": "1.0", "id": "lnd-node", "method": "getbalance", "params": ["*", 6]}' -H 'content-type: text/plain;' http://bitcoind:18443/ | jq -e ".result > 0" > /dev/null 2>&1 do echo -n "." sleep 1 diff --git a/code/docker/run-payment-demo.sh b/code/docker/run-payment-demo.sh new file mode 100755 index 0000000..b72537a --- /dev/null +++ b/code/docker/run-payment-demo.sh @@ -0,0 +1,177 @@ +#!/bin/bash + +# +# Helper functions +# + + +# run-in-node: Run a command inside a docker container, using the bash shell +function run-in-node () { + docker exec "$1" /bin/bash -c "${@:2}" +} + +# wait-for-cmd: Run a command repeatedly until it completes/exits successfuly +function wait-for-cmd () { + until "${@}" > /dev/null 2>&1 + do + echo -n "." + sleep 1 + done + echo +} + +# wait-for-node: Run a command repeatedly until it completes successfully, inside a container +# Combining wait-for-cmd and run-in-node +function wait-for-node () { + wait-for-cmd run-in-node $1 "${@:2}" +} + + +# Start the demo +echo "Starting Payment Demo" + +echo "======================================================" +echo +echo "Waiting for nodes to startup" +echo -n "- Waiting for bitcoind startup..." +wait-for-node bitcoind "cli getblockchaininfo | jq -e \".blocks > 101\"" +echo -n "- Waiting for bitcoind mining..." +wait-for-node bitcoind "cli getbalance | jq -e \". > 50\"" +echo -n "- Waiting for Alice startup..." +wait-for-node Alice "cli getinfo" +echo -n "- Waiting for Bob startup..." +wait-for-node Bob "cli getinfo" +echo -n "- Waiting for Chan startup..." +wait-for-node Chan "cli getinfo" +echo -n "- Waiting for Dina startup..." +wait-for-node Dina "cli getinfo" +echo "All nodes have started" + +echo "======================================================" +echo +echo "Getting node IDs" +alice_address=$(run-in-node Alice "cli getinfo | jq -r .identity_pubkey") +bob_address=$(run-in-node Bob "cli getinfo | jq -r .id") +chan_address=$(run-in-node Chan "cli getinfo| jq -r .nodeId") +dina_address=$(run-in-node Dina "cli getinfo | jq -r .identity_pubkey") + +# Show node IDs +echo "- Alice: ${alice_address}" +echo "- Bob: ${bob_address}" +echo "- Chan: ${chan_address}" +echo "- Dina: ${dina_address}" + +echo "======================================================" +echo +echo "Waiting for Lightning nodes to sync the blockchain" +echo -n "- Waiting for Alice chain sync..." +wait-for-node Alice "cli getinfo | jq -e \".synced_to_chain == true\"" +echo -n "- Waiting for Bob chain sync..." +wait-for-node Bob "cli getinfo | jq -e \".blockheight > 100\"" +echo -n "- Waiting for Chan chain sync..." +wait-for-node Chan "cli getinfo | jq -e \".blockHeight > 100\"" +echo -n "- Waiting for Dina chain sync..." +wait-for-node Dina "cli getinfo | jq -e \".synced_to_chain == true\"" +echo "All nodes synched to chain" + +echo "======================================================" +echo +echo "Setting up connections and channels" +echo "- Alice to Bob" + +# Connect only if not already connected +run-in-node Alice "cli listpeers | jq -e '.peers[] | select(.pub_key == \"${bob_address}\")' > /dev/null" \ +&& { + echo "- Alice already connected to Bob" +} || { + echo "- Open connection from Alice node to Bob's node" + wait-for-node Alice "cli connect ${bob_address}@Bob" +} + +# Create channel only if not already created +run-in-node Alice "cli listchannels | jq -e '.channels[] | select(.remote_pubkey == \"${bob_address}\")' > /dev/null" \ +&& { + echo "- Alice->Bob channel already exists" +} || { + echo "- Create payment channel Alice->Bob" + wait-for-node Alice "cli openchannel ${bob_address} 1000000" +} +echo "Bob to Chan" +run-in-node Bob "cli listpeers | jq -e '.peers[] | select(.id == \"${chan_address}\")' > /dev/null" \ +&& { + echo "- Bob already connected to Chan" +} || { + echo "- Open connection from Bob's node to Chan's node" + wait-for-node Bob "cli connect ${chan_address}@Chan" +} +run-in-node Bob "cli listchannels | jq -e '.channels[] | select(.destination == \"${chan_address}\")' > /dev/null" \ +&& { + echo "- Bob->Chan channel already exists" +} || { + echo "- Create payment channel Bob->Chan" + wait-for-node Bob "cli fundchannel ${chan_address} 1000000" +} +echo "Chan to Dina" +run-in-node Chan "cli peers | jq -e '.[] | select(.nodeId == \"${dina_address}\" and .state == \"CONNECTED\")' > /dev/null" \ +&& { + echo "- Chan already connected to Dina" +} || { + echo "- Open connection from Chan's node to Dina's node" + wait-for-node Chan "cli connect --uri=${dina_address}@Dina" +} +run-in-node Chan "cli channels | jq -e '.[] | select(.nodeId == \"${dina_address}\" and .state == \"NORMAL\")' > /dev/null" \ +&& { + echo "- Chan->Dina channel already exists" +} || { + echo "- Create payment channel Chan->Dina" + wait-for-node Chan "cli open --nodeId=${dina_address} --fundingSatoshis=1000000" +} +echo "All channels created" +echo "======================================================" +echo +echo "Waiting for channels to be confirmed on the blockchain" +echo -n "- Waiting for Alice channel confirmation..." +wait-for-node Alice "cli listchannels | jq -e '.channels[] | select(.remote_pubkey == \"${bob_address}\" and .active == true)'" +echo "- Alice->Bob connected" +echo -n "- Waiting for Bob channel confirmation..." +wait-for-node Bob "cli listchannels | jq -e '.channels[] | select(.destination == \"${chan_address}\" and .active == true)'" +echo "- Bob->Chan connected" +echo -n "- Waiting for Chan channel confirmation..." +wait-for-node Chan "cli channels | jq -e '.[] | select (.nodeId == \"${dina_address}\" and .state == \"NORMAL\")' > /dev/null" +echo "- Chan->Dina connected" +echo "All channels confirmed" + + +echo "======================================================" +echo -n "Check Alice's route to Dina: " +run-in-node Alice "cli queryroutes --dest \"${dina_address}\" --amt 10000" > /dev/null 2>&1 \ +&& { + echo "Alice has a route to Dina" +} || { + echo "Alice doesn't yet have a route to Dina" + echo "Waiting for Alice graph sync. This may take a while..." + wait-for-node Alice "cli describegraph | jq -e '.edges | select(length >= 1)'" + echo "- Alice knows about 1 channel" + wait-for-node Alice "cli describegraph | jq -e '.edges | select(length >= 2)'" + echo "- Alice knows about 2 channels" + wait-for-node Alice "cli describegraph | jq -e '.edges | select(length == 3)'" + echo "- Alice knows about all 3 channels!" + echo "Alice knows about all the channels" +} + +echo "======================================================" +echo +echo "Get 10k sats invoice from Dina" +dina_invoice=$(run-in-node Dina "cli addinvoice 10000 | jq -r .payment_request") +echo "- Dina invoice: " +echo ${dina_invoice} + +echo "======================================================" +echo +echo "Attempting payment from Alice to Dina" +run-in-node Alice "cli payinvoice --json --force ${dina_invoice} | jq -e '.failure_reason == \"FAILURE_REASON_NONE\"'" > /dev/null && { + echo "Successful payment!" +} || +{ + echo "Payment failed" +} diff --git a/code/docker/setup-channels.sh b/code/docker/setup-channels.sh deleted file mode 100644 index 250b6b6..0000000 --- a/code/docker/setup-channels.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/bash - -echo Getting node IDs -alice_address=$(docker-compose exec -T Alice bash -c "lncli -n regtest getinfo | jq -r .identity_pubkey") -bob_address=$(docker-compose exec -T Bob bash -c "lightning-cli getinfo | jq -r .id") -chan_address=$(docker-compose exec -T Chan bash -c "eclair-cli -s -j -p eclair getinfo| jq -r .nodeId") -dina_address=$(docker-compose exec -T Dina bash -c "lncli -n regtest getinfo | jq -r .identity_pubkey") - -# Let's tell everyone what we found! -echo Alice: ${alice_address} -echo Bob: ${bob_address} -echo Chan: ${chan_address} -echo Dina: ${dina_address} - -echo Setting up channels... -echo Alice to Bob -docker-compose exec -T Alice lncli -n regtest connect ${bob_address}@Bob -docker-compose exec -T Alice lncli -n regtest openchannel ${bob_address} 1000000 - -echo Bob to Chan -docker-compose exec -T Bob lightning-cli connect ${chan_address}@Chan -docker-compose exec -T Bob lightning-cli fundchannel ${chan_address} 1000000 - -echo Chan to Dina -docker-compose exec -T Chan eclair-cli -p eclair connect --uri=${dina_address}@Dina -docker-compose exec -T Chan eclair-cli -p eclair open --nodeId=${dina_address} --fundingSatoshis=1000000 - -echo Get 10k sats invoice from Dina -dina_invoice=$(docker-compose exec -T Dina bash -c "lncli -n regtest addinvoice 10000 | jq -r .payment_request") - -echo Dina invoice ${dina_invoice} - -echo Wait for channel establishment - 60 seconds for 6 blocks -sleep 60 - -echo Alice pays Dina 10k sats, routed around the network -docker-compose exec -T Alice lncli -n regtest payinvoice --json --inflight_updates -f ${dina_invoice}