diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f754cbe..9bfb838 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,10 +4,10 @@ jobs: regtest: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v2 - name: Run tests run: | - git clone https://github.com/lnbits/lnbits.git - docker build -t lnbits/lnbits lnbits - chmod +x ./start-regtest - ./start-regtest + git clone https://github.com/lnbits/lnbits-legend.git + docker build -t lnbitsdocker/lnbits-legend lnbits-legend + chmod +x ./tests + ./tests diff --git a/.gitignore b/.gitignore index e60bd79..c542600 100644 --- a/.gitignore +++ b/.gitignore @@ -1,13 +1,7 @@ data - !data/boltz data/boltz/* !data/boltz/boltz.conf - -!data/boltz-client -data/boltz-client/* -!data/boltz-client/boltz.toml - !data/electrs data/electrs/* !data/electrs/config.toml diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 94e7945..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1,90 +0,0 @@ -# CLAUDE.md - -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. - -## Overview - -This is a Bitcoin Lightning Network regtest environment for local development and testing. It provides a Docker Compose setup with multiple interconnected Lightning nodes (LND, Core Lightning, Eclair), Bitcoin Core, Elements/Liquid sidechain, and supporting services like LNbits, Boltz, and Electrs. - -## Common Commands - -### Start the Environment -```sh -./start-regtest # Start all services and run initialization tests -source docker-scripts.sh # Load helper functions for CLI access -``` - -### Stop the Environment -```sh -source docker-scripts.sh -lnbits-regtest-stop # Stops containers and cleans up node data -``` - -### CLI Helpers (after sourcing docker-scripts.sh) -```sh -bitcoin-cli-sim -generate 1 # Mine blocks -lightning-cli-sim 1 getinfo # CLN node 1 (use 1, 2, or 3) -lncli-sim 1 getinfo # LND node 1 (use 1, 2, 3, or 4) -elements-cli-sim getinfo # Elements/Liquid -boltzcli-sim getinfo # Boltz client -``` - -### View Logs -```sh -docker logs lnbits-lnbits-1 -f -docker logs lnbits-lnd-1-1 -f -docker logs lnbits-clightning-1-1 -f -docker logs lnbits-boltz-1 -f -``` - -## Architecture - -### Lightning Network Topology -- **lnd-1**: Hub node with channels to all other nodes. Used for local LNbits testing -- **lnd-2**: Used for Boltz backend swaps -- **lnd-3**: Backend for the dockerized LNbits instance -- **lnd-4**: Standalone node -- **cln-1, cln-2, cln-3**: Core Lightning nodes. cln-2 has REST API via clightning-2-rest -- **eclair-1**: Eclair node with channels from lnd-1 and lnd-2 - -### Channel Graph -lnd-1 is the central hub connected to: lnd-2, lnd-3, cln-1, cln-2, cln-3, eclair-1. Additional connections: lnd-2→cln-2, lnd-3→cln-3, lnd-3→cln-1, lnd-2→eclair-1 - -### Supporting Services -- **bitcoind**: Regtest Bitcoin Core (RPC on 18443) -- **elementsd**: Liquid regtest sidechain (RPC on 18884) -- **electrs**: Bitcoin Electrum server (ports 19001, 3002) -- **electrs-liquid**: Liquid Electrum server (ports 19002, 3003) -- **boltz + boltz-client**: Swap service with Postgres backend -- **lnbits**: Lightning wallet/app platform (port 5001) -- **litd**: Lightning Terminal connected to lnd-1 (ports 8443, 8080) -- **fava**: Beancount accounting interface (port 3333) - -## Web Interfaces -- LNbits: http://localhost:5001/ -- Mempool (via electrs): http://localhost:3002/ -- Boltz API: http://localhost:9001/ -- Lightning Terminal: https://localhost:8443/ (password: testpassword123) -- Fava: http://localhost:3333/ -- Eclair API: http://localhost:8082/ (password: lnbits) - -## Configuration for Local LNbits Development - -When running LNbits locally against this regtest: - -```sh -# LND backend -LNBITS_BACKEND_WALLET_CLASS="LndRestWallet" -LND_REST_ENDPOINT=https://127.0.0.1:8081/ -LND_REST_CERT=./docker/data/lnd-1/tls.cert -LND_REST_MACAROON=./docker/data/lnd-1/data/chain/bitcoin/regtest/admin.macaroon - -# Or CLN backend -LNBITS_BACKEND_WALLET_CLASS="CoreLightningWallet" -CORELIGHTNING_RPC=./docker/data/clightning-1/regtest/lightning-rpc -``` - -## Data Persistence -- Node data stored in `./data//` -- `lnbits-regtest-stop` cleans up Lightning node directories but preserves config files -- Bitcoin/Elements data uses Docker volumes (cleaned on stop) diff --git a/README.md b/README.md index 2b68d3f..4ffa6df 100644 --- a/README.md +++ b/README.md @@ -22,8 +22,8 @@ docker build -t lnbits/lnbits . mkdir docker git clone https://github.com/lnbits/legend-regtest-enviroment.git docker cd docker -chmod +x ./start-regtest -./start-regtest # start the regtest and also run tests +chmod +x ./tests +./tests # start the regtest and also run tests sudo chown -R $USER ./data # Give the data file permissions for user ``` @@ -52,10 +52,10 @@ make dev # testing ```sh -chmod +x ./start-regtest -./start-regtest +chmod +x ./tests +./tests # short answer :) -./start-regtest && echo "PASSED" || echo "FAILED" > /dev/null +./tests && echo "PASSED" || echo "FAILED" > /dev/null ``` usage of the `bitcoin-cli-sim`, `lightning-cli-sim` and `lncli-sim` aliases diff --git a/data/boltz-client/boltz.toml b/data/boltz-client/boltz.toml deleted file mode 100644 index 0045820..0000000 --- a/data/boltz-client/boltz.toml +++ /dev/null @@ -1,49 +0,0 @@ -standalone = true -network = "regtest" - -# Path the the log file -logfile = "" - -electrumUrl = "electrs:19001" -electrumLiquidUrl = "electrs-liquid:19002" - -[BOLTZ] -# By default the daemon automatically connects to the official Boltz instance for the network LND is on -# This value is used to override that -url = "http://boltz-nginx:9001" - -[DATABASE] -# Path to the SQLite database file -# path = "/home/michael/test.db" - -[RPC] -# Host of -host = "0.0.0.0" - -# Port of the gRPC interface -port = 9002 - -# Whether the REST proxy for the gRPC interface should be disabled -restDisabled = false - -# Host of the REST proxy -restHost = "0.0.0.0" - -# Port of the REST proxy -restPort = 9003 - -# Path to the TLS cert for the gRPC and REST interface -tlsCert = "" - -# Path to the TLS private key for the gRPC and REST interface -tlsKey = "" -noTls = true - -# Whether the macaroon authentication for the gRPC and REST interface should be disabled -noMacaroons = true - -# Path to the admin macaroon for the gRPC and REST interface -adminMacaroonPath = "" - -# Path to the read only macaroon for the gRPC and REST interface -readOnlyMacaroonPath = "" diff --git a/data/boltz-nginx/default.conf b/data/boltz-nginx/default.conf deleted file mode 100644 index 116fca2..0000000 --- a/data/boltz-nginx/default.conf +++ /dev/null @@ -1,49 +0,0 @@ -upstream boltz { - server boltz:9001; -} - -upstream boltzr { - server boltz:9005; -} - -upstream ws { - server boltz:9004; -} - -server { - listen 9001; - listen [::]:9001; - server_name localhost; - - add_header Access-Control-Allow-Origin "*" always; - add_header Access-Control-Allow-Methods 'GET, PATCH, DELETE, POST, OPTIONS' always; - add_header Access-Control-Allow-Headers "*" always; - - proxy_set_header Host $http_host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - - if ($request_method = OPTIONS) { - return 204; - } - - location /v2/ws { - proxy_pass http://ws/; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "Upgrade"; - } - - location ~ ^/v2/(lightning|swap/rescue|swap/restore) { - proxy_pass http://boltzr; - } - - location /streamswapstatus { - proxy_pass http://boltzr; - } - - location / { - proxy_pass http://boltz; - } -} diff --git a/data/boltz/boltz.conf b/data/boltz/boltz.conf deleted file mode 100644 index 6a3ef7c..0000000 --- a/data/boltz/boltz.conf +++ /dev/null @@ -1,111 +0,0 @@ -[api] -host = "0.0.0.0" -port = 9_001 - -[grpc] -host = "0.0.0.0" -port = 9_000 - -[postgres] -host = "boltz-postgres" -port = 5432 -database = "boltz" -username = "boltz" -password = "boltz" - -[sidecar] - [sidecar.grpc] - host = "127.0.0.1" - port = 9003 - - [sidecar.ws] - host = "0.0.0.0" - port = 9004 - - [sidecar.api] - host = "0.0.0.0" - port = 9005 - -[[pairs]] -isLegacy = true -base = "BTC" -quote = "BTC" -rate = 1 -fee = 0.5 -swapInFee = 0.1 -maxSwapAmount = 40_294_967 -minSwapAmount = 50_000 - - [pairs.timeoutDelta] - chain = 1440 - reverse = 1440 - swapMinimal = 1440 - swapMaximal = 2880 - swapTaproot = 10080 - -[[pairs]] -isLegacy = true -base = "L-BTC" -quote = "BTC" -fee = 0.25 -swapInFee = 0.1 -rate = 1 -maxSwapAmount = 40_294_967 -minSwapAmount = 100 - - [pairs.submarineSwap] - minSwapAmount = 1_000 - minBatchedAmount = 21 - - [pairs.chainSwap] - minSwapAmount = 25_000 - - [pairs.timeoutDelta] - chain = 1440 - reverse = 1440 - swapMinimal = 1440 - swapMaximal = 2880 - swapTaproot = 10080 - -[[currencies]] -symbol = "BTC" -network = "bitcoinRegtest" -minWalletBalance = 10_000_000 -minChannelBalance = 10_000_000 -maxSwapAmount = 40_294_967 -minSwapAmount = 10_000 -maxZeroConfAmount = 0 - - [currencies.chain] - # mempoolSpace = "http://mempool-web:8090/api" - host = "bitcoind" - zmqpubrawtx = "tcp://bitcoind:29000" - zmqpubrawblock = "tcp://bitcoind:29001" - port = 18_443 - cookie = "/root/.bitcoin/regtest/.cookie" - - wallet = "lnbits" - - [currencies.lnd] - host = "lnd-2" - port = 10_009 - certpath = "/data/lnd/tls.cert" - macaroonpath = "/data/lnd/data/chain/bitcoin/regtest/admin.macaroon" - - -[liquid] -symbol = "L-BTC" -network = "liquidRegtest" - -maxSwapAmount = 40_294_967 -minSwapAmount = 10_000 -maxZeroConfAmount = 40_294_967 - - [liquid.chain] - host = "elementsd" - port = 18884 - cookie = "/root/.elements/liquidregtest/.cookie" - zmqpubrawtx = "tcp://elementsd:31000" - zmqpubhashblock = "tcp://elementsd:31002" - - wallet = "lnbits" diff --git a/docker-compose.yml b/docker-compose.yml index 232b814..08f4550 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,100 +7,22 @@ services: image: lnbits/lnbits restart: on-failure user: "0:0" + entrypoint: "sh -c 'sleep 30; uv run lnbits'" environment: - LNBITS_PORT: 5001 + HOST: lnbits + PORT: 5001 DEBUG: true - LNBITS_ADMIN_UI: true + LNBITS_ADMIN_UI: false LNBITS_BACKEND_WALLET_CLASS: "LndRestWallet" LNBITS_DATA_FOLDER: "./data" - LNBITS_EXTENSIONS_PATH: "/shared" LND_REST_ENDPOINT: "https://lnd-3:8081/" LND_REST_CERT: "./lnd/tls.cert" LND_REST_MACAROON: "./lnd/data/chain/bitcoin/regtest/admin.macaroon" ports: - 5001:5001 volumes: - - ./data/lnbits:/app/data + - lnbits-data:/app/data - ./data/lnd-3:/app/lnd:uid=1000,gid=1000 - - /home/padreug/dev/shared/extensions:/shared/extensions - - fava: - hostname: fava - build: ./fava - restart: on-failure - ports: - - 3333:5000 - volumes: - - ./data/fava:/bean - - boltz: - hostname: boltz - depends_on: - - lnd-2 - - boltz-postgres - restart: always - image: boltz/boltz:latest - ports: - - 9000:9000 - entrypoint: "sh -c 'sleep 30; /boltz-backend/bin/boltzd'" - volumes: - - ./data/lnd-2:/data/lnd/ - - ./data/boltz/:/root/.boltz/ - - elements-data:/root/.elements - - bitcoin-data:/root/.bitcoin - - boltz-client: - hostname: boltz-client - depends_on: - - boltz - restart: always - image: boltz/boltz-client:latest - ports: - - 9002:9002 - - 9003:9003 - expose: - - 9002 - healthcheck: - test: ['CMD', 'boltzcli', '--host', 'boltz-client', 'getinfo'] - interval: 5s - timeout: 3s - retries: 10 - start_period: 0s - volumes: - - elements-data:/root/.elements - - ./data/boltz-client:/root/.boltz - - boltz-backend-nginx: - hostname: boltz-nginx - restart: always - image: nginx:latest - ports: - - 9001:9001 - volumes: - - nginx-data:/etc/nginx/conf.d - healthcheck: - test: ['CMD-SHELL', 'curl http://localhost:9001/version'] - timeout: 1s - retries: 10 - interval: 1s - start_period: 0s - - boltz-postgres: - hostname: boltz-postgres - restart: always - image: postgres:14-alpine - healthcheck: - test: ["CMD-SHELL", "pg_isready --dbname boltz --username boltz"] - interval: 5s - timeout: 30s - retries: 10 - start_period: 5s - environment: - - POSTGRES_DB=boltz - - POSTGRES_USER=boltz - - POSTGRES_PASSWORD=boltz - expose: - - 5432 bitcoind: hostname: bitcoind @@ -222,7 +144,7 @@ services: hostname: lnd-1 depends_on: - bitcoind - image: boltz/lnd:0.19.3-beta + image: boltz/lnd:0.18.4-beta restart: on-failure command: - --listen=lnd-1:9735 @@ -237,7 +159,6 @@ services: - --bitcoind.zmqpubrawblock=bitcoind:29001 - --noseedbackup - --protocol.wumbo-channels - - --rpcmiddleware.enable expose: - 8081 - 9735 @@ -250,7 +171,7 @@ services: hostname: lnd-2 depends_on: - bitcoind - image: boltz/lnd:0.19.3-beta + image: boltz/lnd:0.18.4-beta restart: on-failure command: - --listen=lnd-2:9735 @@ -330,39 +251,6 @@ services: - ./data/lnd-4:/root/.lnd/ - bitcoin-data:/root/.bitcoin - litd: - hostname: litd - depends_on: - - lnd-1 - image: lightninglabs/lightning-terminal:v0.16.0-alpha - restart: on-failure - entrypoint: /bin/sh - command: - - -c - - | - echo "Waiting for LND to be ready..." - while ! nc -z lnd-1 10009 2>/dev/null; do - echo "Waiting for lnd-1:10009..." - sleep 2 - done - sleep 5 - exec /bin/litd \ - --httpslisten=0.0.0.0:8443 \ - --insecure-httplisten=0.0.0.0:8080 \ - --uipassword=testpassword123 \ - --network=regtest \ - --lnd-mode=remote \ - --remote.lnd.rpcserver=lnd-1:10009 \ - --remote.lnd.macaroonpath=/root/.lnd/data/chain/bitcoin/regtest/admin.macaroon \ - --remote.lnd.tlscertpath=/root/.lnd/tls.cert \ - --autopilot.disable - ports: - - 8443:8443 - - 8080:8080 - volumes: - - ./data/lnd-1:/root/.lnd - - ./data/litd:/root/.lit - eclair: hostname: eclair depends_on: @@ -467,12 +355,6 @@ services: - elements-data:/root/.elements volumes: + lnbits-data: bitcoin-data: elements-data: - nginx-data: - name: nginx-data - driver: local - driver_opts: - type: none - o: bind - device: ./data/boltz-nginx/ diff --git a/docker-scripts.sh b/docker-scripts.sh index eca62fd..7e64274 100644 --- a/docker-scripts.sh +++ b/docker-scripts.sh @@ -1,10 +1,6 @@ #!/bin/sh export COMPOSE_PROJECT_NAME=lnbits -boltzcli-sim() { - docker exec -it lnbits-boltz-client-1 boltzcli "$@" -} - bitcoin-cli-sim() { docker exec lnbits-bitcoind-1 bitcoin-cli -regtest "$@" } @@ -51,14 +47,6 @@ wait-for-eclair-channel() { done } -# args(i) -fund_boltz_client() { - # first address of seed: abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about - address="el1qq2xvpcvfup5j8zscjq05u2wxxjcyewk7979f3mmz5l7uw5pqmx6xf5xy50hsn6vhkm5euwt72x878eq6zxx2z0z676mna6kdq" - echo "funding: $address on boltz-client" - elements-cli-sim -named sendtoaddress address=$address amount=30 fee_rate=100 > /dev/null -} - # args(i) fund_clightning_node() { @@ -110,7 +98,7 @@ lnbits-regtest-start-log(){ lnbits-regtest-stop(){ docker compose down --volumes # clean up lightning node data - sudo rm -rf ./data/clightning-1 ./data/clightning-2 ./data/clightning-3 ./data/lnd-1 ./data/lnd-2 ./data/lnd-3 ./data/lnd-4 ./data/boltz/boltz.db ./data/eclair/regtest ./data/boltz-client/liquid-wallet ./data/boltz-client/bitcoin-wallet ./data/boltz-client/wallet ./data/boltz-client/boltz.db + sudo rm -rf ./data/clightning-1 ./data/clightning-2 ./data/clightning-3 ./data/lnd-1 ./data/lnd-2 ./data/lnd-3 ./data/lnd-4 ./data/boltz/boltz.db ./data/eclair/regtest # recreate lightning node data folders preventing permission errors mkdir ./data/clightning-1 ./data/clightning-2 ./data/clightning-3 ./data/lnd-1 ./data/lnd-2 ./data/lnd-3 ./data/lnd-4 } @@ -120,13 +108,6 @@ lnbits-regtest-restart(){ lnbits-regtest-start } -boltz-client-init(){ - for i in 0 1 2; do - fund_boltz_client - done - elements-cli-sim -generate 3 > /dev/null -} - lnbits-bitcoin-init(){ echo "init_bitcoin_wallet..." bitcoin-cli-sim createwallet lnbits || bitcoin-cli-sim loadwallet lnbits @@ -139,7 +120,6 @@ lnbits-elements-init(){ elements-cli-sim createwallet lnbits || elements-cli-sim loadwallet lnbits echo "mining 150 blocks..." elements-cli-sim -generate 150 > /dev/null - elements-cli-sim rescanblockchain } lnbits-init(){ @@ -152,7 +132,6 @@ lnbits-regtest-init(){ lnbits-elements-init lnbits-lightning-sync lnbits-lightning-init - boltz-client-init lnbits-init } diff --git a/fava/Dockerfile b/fava/Dockerfile deleted file mode 100644 index 83f2fbb..0000000 --- a/fava/Dockerfile +++ /dev/null @@ -1,10 +0,0 @@ -FROM python:3.12-slim - -RUN pip install --no-cache-dir fava==1.30.11 - -WORKDIR /bean - -EXPOSE 5000 - -ENTRYPOINT ["fava"] -CMD ["-H", "0.0.0.0", "/bean/ledger.beancount"] diff --git a/lndconnect.sh b/lndconnect.sh deleted file mode 100755 index 7af3378..0000000 --- a/lndconnect.sh +++ /dev/null @@ -1,110 +0,0 @@ -#!/bin/bash -# -# Generate lndconnect QR strings for Zeus wallet connection -# -# Usage: -# ./lndconnect.sh [node-number] -# -# Examples: -# ./lndconnect.sh 1 # lnd-1 (requires port exposure) -# ./lndconnect.sh 3 # lnd-3 (REST port 8081 exposed by default) -# ./lndconnect.sh 4 # lnd-4 (Lightning.Pub's node, requires port exposure) -# - -set -e - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -NODE_NUM="${1:-3}" -DATA_DIR="$SCRIPT_DIR/data/lnd-$NODE_NUM" - -# Colors -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -RED='\033[0;31m' -NC='\033[0m' - -log() { echo -e "${GREEN}[lndconnect]${NC} $1"; } -warn() { echo -e "${YELLOW}[lndconnect]${NC} $1"; } -error() { echo -e "${RED}[lndconnect]${NC} $1"; exit 1; } - -# Get local IP -get_local_ip() { - ip route get 1 2>/dev/null | awk '{print $7; exit}' || hostname -I | awk '{print $1}' -} - -# Base64url encode (no padding, URL-safe) -base64url_encode() { - base64 -w0 | tr '+/' '-_' | tr -d '=' -} - -# Check if node data exists -if [ ! -d "$DATA_DIR" ]; then - error "Node data directory not found: $DATA_DIR" -fi - -MACAROON_PATH="$DATA_DIR/data/chain/bitcoin/regtest/admin.macaroon" -CERT_PATH="$DATA_DIR/tls.cert" - -if [ ! -f "$MACAROON_PATH" ]; then - error "Macaroon not found: $MACAROON_PATH" -fi - -if [ ! -f "$CERT_PATH" ]; then - error "TLS cert not found: $CERT_PATH" -fi - -# Get host IP -HOST_IP=$(get_local_ip) - -# Determine REST port based on node -case $NODE_NUM in - 3) REST_PORT=8081 ;; # Exposed in docker-compose - *) - warn "lnd-$NODE_NUM REST port may not be exposed to host." - warn "You may need to add port mapping to docker-compose.yml" - REST_PORT=8081 - ;; -esac - -log "Generating lndconnect for lnd-$NODE_NUM..." -log "Host: $HOST_IP:$REST_PORT" - -# Encode macaroon and cert -MACAROON_B64=$(cat "$MACAROON_PATH" | base64url_encode) -CERT_B64=$(cat "$CERT_PATH" | base64url_encode) - -# Build lndconnect URL -LNDCONNECT_URL="lndconnect://${HOST_IP}:${REST_PORT}?macaroon=${MACAROON_B64}&cert=${CERT_B64}" - -echo "" -echo "============================================================" -echo "lndconnect URL for lnd-$NODE_NUM:" -echo "============================================================" -echo "" -echo "$LNDCONNECT_URL" -echo "" - -# Generate QR code if qrencode is available -if command -v qrencode &>/dev/null; then - log "Generating QR code..." - echo "" - qrencode -t ANSIUTF8 "$LNDCONNECT_URL" - echo "" -else - warn "Install 'qrencode' for terminal QR code: sudo pacman -S qrencode" -fi - -echo "============================================================" -echo "Instructions:" -echo "============================================================" -echo "1. Open Zeus wallet" -echo "2. Go to Settings → Add a new node" -echo "3. Scan QR or paste the lndconnect URL" -echo "" -if [ "$NODE_NUM" != "3" ]; then - echo "NOTE: lnd-$NODE_NUM REST port is not exposed by default." - echo "Add this to docker-compose.yml under lnd-$NODE_NUM:" - echo " ports:" - echo " - '808$NODE_NUM:8081'" - echo "" -fi diff --git a/start-regtest b/tests similarity index 96% rename from start-regtest rename to tests index 82a5ad6..b547666 100755 --- a/start-regtest +++ b/tests @@ -57,7 +57,6 @@ done run "eclair-1 openchannels" 2 $(docker exec lnbits-eclair-1 curl -s http://localhost:8080/channels -X POST -u :lnbits| jq '. | length') run "eclair-1 blockHeight" $blockheight $(docker exec lnbits-eclair-1 curl -s http://localhost:8080/getinfo -X POST -u :lnbits| jq '.blockHeight') run "lnbits service status" "200" $(curl -s -o /dev/null -w "%{http_code}" "http://localhost:5001/") -run "boltz service status" "200" $(curl -s -o /dev/null --head -w "%{http_code}" "http://localhost:9001/version") # return non-zero exit code if a test fails if [[ "$failed" == "true" ]]; then