diff --git a/docker/README.md b/docker/README.md index 8b1378917..38a465450 100644 --- a/docker/README.md +++ b/docker/README.md @@ -1 +1,152 @@ +### Getting started +This document is written for people who are eager to do something with +lightning network daemon (`lnd`). Current workflow is big because we +recreate the whole network by ourselves, next versions will use the +started `btcd` bitcoin node in `testnet` and `faucet` wallet from which +you will get the bitcoins. +In current readme we decribe the steps which are needed to recreate +following schema, and send payment from `Alice` to `Bob`. +``` ++ ----- + + --- + +| Alice | <--- channel ---> | Bob | <--- Bob and Alice are the lightning network daemons which ++ ----- + + --- + creates the channel and interact with each other using + | | Bitcoin network as source of truth. + | | + + - - - - - + - - - - - - + + | + + ---------------- + + | Bitcoin network | <--- In current scenario for simplicity we create only one + + ---------------- + "btcd" node which represents the Bitcoin network, in + real situation Alice and Bob will likely be + connected to different Bitcoin nodes. +``` + +#### Prerequisites + Name | Version + --------|--------- + docker-compose | 1.9.0 + docker | 1.13.0 + +**General workflow is following:** + * Create Bitcoin network + * Create `Alice` node + * Create `Bob` node + * Initialize `Alice` node with some amount of bitcoins + * Open channel between `Alice` and `Bob` + * Send payment from `Alice` to `Bob` + +Start the `btcd`, create `Alice` address and mine some bitcoins. +```bash +# Create "btcd" node: +$ docker-compose up -d "btcd" + +# Run "Alice" container and log into it: +$ docker-compose up -d "alice" +$ docker exec -i -t "alice" bash + +# Generate new backward compatible "segwit" "alice" address: +alice$ lncli newaddress np2wkh + +# Recreate "btcd" node and set "alice" address as mining address: +$ MINING_ADDRESS= docker-compose up -d "btcd" + +# Generate 201 block (we need at least "100 >=" blocks because of coinbase +# block maturity and "250 >=" in order to activate segwit): +$ docker-compose run btcctl generate 250 + +# Check that segwit is active: +$ docker-compose run btcctl getblockchaininfo | grep -A 1 segwit +``` + +Now we have `btcd` node turned on and some amount of bitcoins mined on +`Alice` address. But in order `lnd` saw them lets restart the `lnd` +daemon. +```bash +# Stop "Alice" container: +$ docker-compose stop "alice" + +# Start "Alice" container and log into it: +$ docker-compose up --no-recreate -d "alice" +$ docker exec -i -t "alice" bash + +# Check "Alice" balance: +alice$ lncli walletbalance --witness_only=true +``` + +Connect `Bob` node to `Alice` node. +```bash +# Run "Bob" node and log into it: +$ docker-compose up --no-recreate -d "bob" +$ docker exec -i -t "bob" bash + +# Get the identity address of "Bob" node: +bob$ lncli getinfo + +# Get the IP address of "Bob" node: +$ docker inspect "bob" | grep IPAddress + +# Connect "Alice" to the "Bob" node: +alice$ lncli connect @:10011 + +# Check list of peers on "Alice" side: +alice$ lncli listpeers + +# Check list of peers on "Bob" side: +bob$ lncli listpeers +``` + +Create `Alice<->Bob` channel. +```bash +# Open the channel with "Bob": +alice$ lncli openchannel --node_key= --num_confs=1 --local_amt=1000000 --push_amt=0 + +# Include funding transaction in block thereby open the channel: +$ docker-compose run btcctl generate 1 + +# Check that channel with "Bob" was created: +alice$ lncli listchannels +``` + +Send the payment form "Alice" to "Bob". +``` +# Add invoice on "Bob" side: +bob> lncli addinvoice --value=10000 + +# Send payment from "Alice" to "Bob": +alice> lncli sendpayment --pay_req= + +# Check "Alice" channel balance was descreased on sent amount: +alice> lncli listchannels + +# Check "Bob" channel balance was increased on sent amount: +bob> lncli listchannels +``` + +Now we have open channel in which we sent only one payment, lets imagine +that we sent a lots of them and we ok to close channel. Lets do it. +``` +# List the "Alice" channel and retrieve "channel_point" which represent +# the opened channel: +alice> lncli listchannels + +# Channel point consist of two numbers devided by ":" the first on is +# "funding_txid" and the second one is "output_index": +alice> lncli closechannel --funding_txid= --output_index= + +# Include close transaction in block thereby close the channel: +$ docker-compose run btcctl generate 1 + +# Check "Alice" on-chain balance was descresed on sent amount: +alice> lncli walletbalance + +# Check "Bob" on-chain balance was increased on sent amount: +bob> lncli walletbalance +``` + + +### Questions +* How to see `alice` | `bob` | `btcd` logs? +``` +docker-compose logs +``` \ No newline at end of file diff --git a/docker/btcd/Dockerfile b/docker/btcd/Dockerfile index 9c6c9ec50..6dc991f94 100644 --- a/docker/btcd/Dockerfile +++ b/docker/btcd/Dockerfile @@ -2,27 +2,36 @@ FROM golang:1.7 MAINTAINER Olaoluwa Osuntokun +# Expose mainnet ports (server, rpc) +EXPOSE 8333 8334 + +# Expose testnet ports (server, rpc) +EXPOSE 18333 18334 + +# Expose simnet ports (server, rpc) +EXPOSE 18555 18556 + +# Expose segnet ports (server, rpc) +EXPOSE 28901 28902 + # Grab and install the latest version of roasbeef's fork of btcd and all # related dependencies. RUN go get -u -v github.com/roasbeef/btcd/... -# Expose the mainnet, testnet, simnet, and segnet listening ports. -EXPOSE 8333 18333 18335 28901 +RUN mkdir "/rpc" "/root/.btcd" "/root/.btcctl" +RUN touch "/root/.btcd/btcd.conf" -# Expose the mainnet, testnet, simnet, and segnet rpc ports. -EXPOSE 8333 18333 18336 28902 +# Manually generate certificate and add all domains, it is needed to connect +# "btcctl" and "lnd" to "btcd" over docker links. +RUN "/go/bin/gencerts" --host="*" --directory="/rpc" --force -# Create a volume to house the RPC credentials. This will be shared with any -# lnd containers so they can securely query btcd's RPC server. +# Create a volume to house pregenerated RPC credentials. This will be +# shared with any lnd, btcctl containers so they can securely query btcd's RPC +# server. +# You should NOT do this before certificate generation! +# Otherwise manually generated certificate will be overriden with shared +# mounted volume! For more info read dockerfile "VOLUME" documentation. VOLUME ["/rpc"] -VOLUME ["/data"] - -RUN mkdir /root/.btcd && mkdir /root/.btcctl - -COPY btcd-start.sh / - -# Finally, execute the shell script that will start btcd. We use a shell script -# rather than executing the command directly with ENTRYPOINT in order to ensure -# environment variables get properly substitued. -ENTRYPOINT ["/btcd-start.sh"] +COPY "start-btcctl.sh" . +COPY "start-btcd.sh" . diff --git a/docker/btcd/btcd-start.sh b/docker/btcd/btcd-start.sh deleted file mode 100755 index 04334c9fe..000000000 --- a/docker/btcd/btcd-start.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - - -/go/bin/btcd --datadir=/data --logdir=/data --simnet \ - --rpccert=/rpc/rpc.cert --rpckey=/rpc/rpc.key \ - --rpcuser=${RPCUSER} --rpcpass=${RPCPASS} --rpclisten=0.0.0.0 \ - --debuglevel=debug diff --git a/docker/btcd/start-btcctl.sh b/docker/btcd/start-btcctl.sh new file mode 100755 index 000000000..761ee3491 --- /dev/null +++ b/docker/btcd/start-btcctl.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash + +# Check env variable and in case of empty value and default value specified +# returns default value, in case of non-empty value returns value. +set_env() { + # docker initialized env variables with blank string and we can't just + # use -z flag as usually. + BLANK_STRING='""' + + VARIABLE="$1" + NAME="$2" + DEFAULT="$3" + + if [[ -z "$VARIABLE" || "$VARIABLE" == "$BLANK_STRING" ]]; then + + if [ -z "$DEFAULT" ]; then + echo "You should specify '$NAME' env variable" + exit 0 + else + VARIABLE="$DEFAULT" + fi + fi + + # echo is used as return in case if string values + echo "$VARIABLE" +} + +RPCUSER=$(set_env "$RPCUSER" "RPCUSER") +RPCPASS=$(set_env "$RPCPASS" "RPCPASS") + +btcctl \ + --simnet \ + --rpccert="/rpc/rpc.cert" \ + --rpcuser="$RPCUSER" \ + --rpcpass="$RPCPASS" \ + --rpcserver="rpcserver" \ + "$@" diff --git a/docker/btcd/start-btcd.sh b/docker/btcd/start-btcd.sh new file mode 100755 index 000000000..a513ba6f5 --- /dev/null +++ b/docker/btcd/start-btcd.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash + +# Check env variable and in case of empty value and default value specified +# returns default value, in case of non-empty value returns value. +set_env() { + # docker initialized env variables with blank string and we can't just + # use -z flag as usually. + BLANK_STRING='""' + + VARIABLE="$1" + NAME="$2" + DEFAULT="$3" + + if [[ -z "$VARIABLE" || "$VARIABLE" == "$BLANK_STRING" ]]; then + + if [ -z "$DEFAULT" ]; then + echo "You should specify '$NAME' env variable" + exit 0 + else + VARIABLE="$DEFAULT" + fi + fi + + # echo is used as return in case if string values + echo "$VARIABLE" +} + +RPCUSER=$(set_env "$RPCUSER" "RPCUSER") +RPCPASS=$(set_env "$RPCPASS" "RPCPASS") + +DEBUG=$(set_env "$DEBUG" "DEBUG") + +MINING_ADDRESS=$(set_env "$MINING_ADDRESS" "MINING_ADDRESS" "ScoDuqH7kYA9nvxuRg4Xk7E31AhsSc5zxp") + +btcd \ + --debuglevel="$DEBUG" \ + --datadir="/data" \ + --logdir="/data" \ + --simnet \ + --rpccert="/rpc/rpc.cert" \ + --rpckey="/rpc/rpc.key" \ + --rpcuser="$RPCUSER" \ + --rpcpass="$RPCPASS" \ + --miningaddr="$MINING_ADDRESS" \ + --rpclisten="0.0.0.0" \ + "$@" + diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index eb62f9e09..310d20acb 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -1,28 +1,56 @@ version: '2' services: + + btc: + image: btcd + build: + context: btcd/ + volumes: + - shared:/rpc + environment: + - RPCUSER="devuser" + - RPCPASS="devpass" + btcd: + extends: btc + container_name: btcd environment: - - RPCUSER=devuser - - RPCPASS=devpass - build: - context: btcd/ - expose: - - "18336" - volumes: - - shared-volume:/rpc - lnd: - environment: - - RPCUSER=devuser - - RPCPASS=devpass - build: - context: ../ - dockerfile: docker/lnd/Dockerfile - expose: - - "10009" - - "10011" - volumes: - - shared-volume:/rpc + - DEBUG="debug" + - MINING_ADDRESS + entrypoint: ["./start-btcd.sh"] + + btcctl: + extends: btc + container_name: btcctl links: + - "btcd:rpcserver" + entrypoint: ["./start-btcctl.sh"] + + lnd: + image: lnd + build: + context: ../ + dockerfile: docker/lnd/Dockerfile + environment: + - RPCUSER="devuser" + - RPCPASS="devpass" + - DEBUG="debug" + volumes: + - shared:/rpc + entrypoint: ["./start-lnd.sh"] + + alice: + extends: lnd + container_name: alice + links: - btcd + + bob: + extends: lnd + container_name: bob + links: + - btcd + volumes: - shared-volume: {} + shared: + driver: local diff --git a/docker/lnd/Dockerfile b/docker/lnd/Dockerfile index bb4effa1e..3907fbad3 100644 --- a/docker/lnd/Dockerfile +++ b/docker/lnd/Dockerfile @@ -13,18 +13,7 @@ ENV GODEBUG netdns=cgo RUN go build RUN go install . ./cmd/... -# Mount a volume where btcd's RPC credentials are stored. We'll need to read -# the TLS cert from this directory. -VOLUME ["/rpc"] +# Expose lnd ports (server, rpc). +EXPOSE 10011 10009 -VOLUME ["/data"] - -# Expose the p2p listening port, and the current RPC port. -EXPOSE 10009 10011 - -COPY docker/lnd/lnd-start.sh / - -# Finally, execute the shell script that will start lnd. We use a shell script -# rather than executing the command directly with ENTRYPOINT in order to ensure -# environment variables get properly substitued. -ENTRYPOINT ["/lnd-start.sh"] +COPY "docker/lnd/start-lnd.sh" . diff --git a/docker/lnd/lnd-start.sh b/docker/lnd/lnd-start.sh deleted file mode 100755 index c5465fa6d..000000000 --- a/docker/lnd/lnd-start.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - -/go/bin/lnd --datadir=/data --logdir=/data --simnet \ - --btcdhost=btcd --rpccert=/rpc/rpc.cert \ - --rpcuser=${RPCUSER} --rpcpass=${RPCPASS} --debuglevel=debug diff --git a/docker/lnd/start-lnd.sh b/docker/lnd/start-lnd.sh new file mode 100755 index 000000000..02385a765 --- /dev/null +++ b/docker/lnd/start-lnd.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash + +# Check env variable and in case of empty value and default value specified +# returns default value, in case of non-empty value returns value. +set_env() { + # docker initialized env variables with blank string and we can't just + # use -z flag as usually. + BLANK_STRING='""' + + VARIABLE="$1" + NAME="$2" + DEFAULT="$3" + + if [[ -z "$VARIABLE" || "$VARIABLE" == "$BLANK_STRING" ]]; then + + if [ -z "$DEFAULT" ]; then + echo "You should specify '$NAME' env variable" + exit 0 + else + VARIABLE="$DEFAULT" + fi + fi + + # echo is used as return in case if string values + echo "$VARIABLE" +} + +RPCUSER=$(set_env "$RPCUSER" "RPCUSER") +RPCPASS=$(set_env "$RPCPASS" "RPCPASS") + +DEBUG=$(set_env "$DEBUG" "DEBUG") + +lnd \ + --datadir="/data" \ + --logdir="/data" \ + --simnet \ + --btcdhost="btcd" \ + --rpccert="/rpc/rpc.cert" \ + --rpcuser="$RPCUSER" \ + --rpcpass="$RPCPASS" \ + --debuglevel="$DEBUG" \ + "$@"