diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index e35c1536b..8f59c7e39 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -55,11 +55,21 @@ jobs: - name: Build and push boltz uses: docker/build-push-action@v5 with: - context: . - file: Dockerfile.boltz + context: docker/boltzclient push: true tags: ${{ secrets.DOCKER_USERNAME }}/lnbits-boltz:${{ inputs.tag }} platforms: linux/amd64,linux/arm64 cache-from: type=local,src=/tmp/.buildx-cache cache-to: type=local,dest=/tmp/.buildx-cache build-args: LNBITS_TAG=${{ inputs.tag }} + + - name: Build and push sparkl2 + uses: docker/build-push-action@v5 + with: + context: docker/sparkl2 + push: true + tags: ${{ secrets.DOCKER_USERNAME }}/lnbits-sparkl2:${{ inputs.tag }} + platforms: linux/amd64,linux/arm64 + cache-from: type=local,src=/tmp/.buildx-cache + cache-to: type=local,dest=/tmp/.buildx-cache + build-args: LNBITS_TAG=${{ inputs.tag }} diff --git a/.github/workflows/regtest.yml b/.github/workflows/regtest.yml index 27b9a20e0..8cb2bcc1c 100644 --- a/.github/workflows/regtest.yml +++ b/.github/workflows/regtest.yml @@ -38,8 +38,7 @@ jobs: - name: Setup Regtest run: | - git clone https://github.com/lnbits/legend-regtest-enviroment.git docker - cd docker + cd docker/regtest chmod +x ./start-regtest ./start-regtest sudo chmod -R a+rwx . @@ -50,16 +49,16 @@ jobs: LNBITS_DATABASE_URL: ${{ inputs.db-url }} LNBITS_BACKEND_WALLET_CLASS: ${{ inputs.backend-wallet-class }} LND_REST_ENDPOINT: https://localhost:8081/ - LND_REST_CERT: ./docker/data/lnd-3/tls.cert - LND_REST_MACAROON: ./docker/data/lnd-3/data/chain/bitcoin/regtest/admin.macaroon + LND_REST_CERT: ./docker/regtest/data/lnd-3/tls.cert + LND_REST_MACAROON: ./docker/regtest/data/lnd-3/data/chain/bitcoin/regtest/admin.macaroon LND_GRPC_ENDPOINT: localhost LND_GRPC_PORT: 10009 - LND_GRPC_CERT: docker/data/lnd-3/tls.cert - LND_GRPC_MACAROON: docker/data/lnd-3/data/chain/bitcoin/regtest/admin.macaroon - CORELIGHTNING_RPC: ./docker/data/clightning-1/regtest/lightning-rpc + LND_GRPC_CERT: ./docker/regtest/data/lnd-3/tls.cert + LND_GRPC_MACAROON: ./docker/regtest/data/lnd-3/data/chain/bitcoin/regtest/admin.macaroon + CORELIGHTNING_RPC: ./docker/regtest/data/clightning-1/regtest/lightning-rpc CORELIGHTNING_REST_URL: https://localhost:3001 - CORELIGHTNING_REST_MACAROON: ./docker/data/clightning-2-rest/access.macaroon - CORELIGHTNING_REST_CERT: ./docker/data/clightning-2-rest/certificate.pem + CORELIGHTNING_REST_MACAROON: ./docker/regtest/data/clightning-2-rest/access.macaroon + CORELIGHTNING_REST_CERT: ./docker/regtest/data/clightning-2-rest/certificate.pem LNBITS_ENDPOINT: http://localhost:5001 LNBITS_KEY: "d08a3313322a4514af75d488bcc27eee" ECLAIR_URL: http://127.0.0.1:8082 diff --git a/.gitignore b/.gitignore index 1957ddb9d..7790682cc 100644 --- a/.gitignore +++ b/.gitignore @@ -42,7 +42,6 @@ lnbits/static/bundle.min.js.old lnbits/static/bundle.min.css.old lnbits/static/bundle-components.min.js.old lnbits/upgrades -docker # Nix *result* diff --git a/Dockerfile.boltz b/docker/boltzclient/Dockerfile similarity index 82% rename from Dockerfile.boltz rename to docker/boltzclient/Dockerfile index 5842a537d..c5d547fe9 100644 --- a/Dockerfile.boltz +++ b/docker/boltzclient/Dockerfile @@ -14,11 +14,13 @@ ENV PATH="/root/.local/bin:$PATH" # Reinstall dependencies just in case (needed for CMD usage) RUN uv sync --all-extras -# LNbits + boltzd configuration +# LNbits ENV LNBITS_PORT="5000" ENV LNBITS_HOST="0.0.0.0" ENV LNBITS_BACKEND_WALLET_CLASS="BoltzWallet" ENV FUNDING_SOURCE_MAX_RETRIES=10 + +# Boltzd ENV BOLTZ_CLIENT_ENDPOINT="127.0.0.1:9002" ENV BOLTZ_CLIENT_MACAROON="/root/.boltz/macaroons/admin.macaroon" ENV BOLTZ_CLIENT_CERT="/root/.boltz/tls.cert" @@ -26,8 +28,7 @@ ENV BOLTZ_CLIENT_WALLET="lnbits" EXPOSE 5000 -# Entrypoint to start boltzd and LNbits -COPY dockerboltz.sh /dockerboltz.sh -RUN chmod +x /dockerboltz.sh +COPY entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh -CMD ["/dockerboltz.sh"] +CMD ["/entrypoint.sh"] diff --git a/dockerboltz.sh b/docker/boltzclient/entrypoint.sh similarity index 100% rename from dockerboltz.sh rename to docker/boltzclient/entrypoint.sh diff --git a/docker/regtest/.gitignore b/docker/regtest/.gitignore new file mode 100644 index 000000000..0105cd28a --- /dev/null +++ b/docker/regtest/.gitignore @@ -0,0 +1,18 @@ +!data +data/* + +!data/boltz +data/boltz/* +!data/boltz/boltz.conf + +!data/boltz-client +data/boltz-client/* +!data/boltz-client/boltz.toml + +!data/boltz-nginx +data/boltz-nginx/* +!data/boltz-nginx/default.conf + +!data/eclair +data/eclair/* +!data/eclair/eclair.conf diff --git a/docker/regtest/README.md b/docker/regtest/README.md new file mode 100644 index 000000000..3cb6a1385 --- /dev/null +++ b/docker/regtest/README.md @@ -0,0 +1,99 @@ +![TESTS](https://github.com/lnbits/legend-regtest-enviroment/actions/workflows/ci.yml/badge.svg) + +# nodes + +- lnd-1: for locally testing your current lnbits +- lnd-2: used for boltz backend +- lnd-3: used for lnbits inside docker +- cln-1: for locally testing your current lnbits +- cln-2: used for clightning-REST +- eclair-1: for locally testing your current lnbits + +# Installing regtest + +get the regtest enviroment ready + +```sh +# Install docker https://docs.docker.com/engine/install/ +# Make sure your user has permission to use docker 'sudo usermod -aG docker ${USER}' then reboot +# Stop/start docker 'sudo systemctl stop docker' 'sudo systemctl start docker' + +sudo apt install jq +git clone https://github.com/lnbits/lnbits.git +cd lnbits +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 +sudo chown -R $USER ./data # Give the data file permissions for user +``` + +# Running LNbits on regtest + +add this ENV variables to your `.env` file + +```sh +DEBUG=true + +# LND +LNBITS_BACKEND_WALLET_CLASS="LndRestWallet" +LND_REST_ENDPOINT=https://127.0.0.1:8081/ +LND_REST_CERT=/home/user/repos/lnbits/docker/data/lnd-1/tls.cert +LND_REST_MACAROON=/home/user/repos/lnbits/docker/data/lnd-1/data/chain/bitcoin/regtest/admin.macaroon + +# CLN +LNBITS_BACKEND_WALLET_CLASS="CoreLightningWallet" +CORELIGHTNING_RPC=./docker/data/clightning-1/regtest/lightning-rpc + + +# Run LNbits +uv run lnbits + +# Run LNbits with hot reload +make dev +``` + +# testing + +```sh +chmod +x ./start-regtest +./start-regtest +# short answer :) +./start-regtest && echo "PASSED" || echo "FAILED" > /dev/null +``` + +usage of the `bitcoin-cli-sim`, `lightning-cli-sim` and `lncli-sim` aliases + +```sh +cd ~/lnbits/docker +source docker-scripts.sh +# use bitcoin core, mine a block +bitcoin-cli-sim -generate 1 + +# use c-lightning nodes +lightning-cli-sim 1 newaddr | jq -r '.bech32' # use node 1 +lightning-cli-sim 2 getinfo # use node 2 +lightning-cli-sim 3 getinfo # use node 3 + +# use lnd nodes +lncli-sim 1 newaddr p2wsh +lncli-sim 2 listpeers +``` + +# urls + +- mempool: http://localhost:8080/ +- boltz api: http://localhost:9001/ +- lnd-1 rest: http://localhost:8081/ +- lnbits: http://localhost:5001/ + +# debugging docker logs + +```sh +docker logs lnbits-lnbits-1 -f +docker logs lnbits-boltz-1 -f +docker logs lnbits-clightning-1-1 -f +docker logs lnbits-lnd-2-1 -f +``` diff --git a/docker/regtest/data/boltz-client/boltz.toml b/docker/regtest/data/boltz-client/boltz.toml new file mode 100644 index 000000000..004582069 --- /dev/null +++ b/docker/regtest/data/boltz-client/boltz.toml @@ -0,0 +1,49 @@ +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/docker/regtest/data/boltz-nginx/default.conf b/docker/regtest/data/boltz-nginx/default.conf new file mode 100644 index 000000000..116fca2d4 --- /dev/null +++ b/docker/regtest/data/boltz-nginx/default.conf @@ -0,0 +1,49 @@ +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/docker/regtest/data/boltz/boltz.conf b/docker/regtest/data/boltz/boltz.conf new file mode 100644 index 000000000..6a3ef7cd0 --- /dev/null +++ b/docker/regtest/data/boltz/boltz.conf @@ -0,0 +1,111 @@ +[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/regtest/data/eclair/eclair.conf b/docker/regtest/data/eclair/eclair.conf new file mode 100644 index 000000000..40f812041 --- /dev/null +++ b/docker/regtest/data/eclair/eclair.conf @@ -0,0 +1,28 @@ +eclair { + chain = "regtest" + + api { + binding-ip = "0.0.0.0" + enabled = true + port = 8080 + password = "lnbits" + } + + bitcoind { + host = "bitcoind" + rpcport = 18443 + auth = "safecookie" + cookie = "/root/.bitcoin/regtest/.cookie" + + zmqblock = "tcp://bitcoind:29002" + zmqtx = "tcp://bitcoind:29000" + } + + channel { + max-funding-satoshis = 10000000000 + } + + features { + option_support_large_channel = mandatory + } +} diff --git a/docker/regtest/docker-compose.yml b/docker/regtest/docker-compose.yml new file mode 100644 index 000000000..f328f1d91 --- /dev/null +++ b/docker/regtest/docker-compose.yml @@ -0,0 +1,434 @@ +services: + + lnbits: + hostname: lnbits + depends_on: + - lnd-3 + image: lnbits/lnbits + restart: on-failure + user: "0:0" + environment: + LNBITS_PORT: 5001 + DEBUG: true + LNBITS_ADMIN_UI: false + LNBITS_BACKEND_WALLET_CLASS: "LndRestWallet" + LNBITS_DATA_FOLDER: "./data" + 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: + - lnbits-data:/app/data + - ./data/lnd-3:/app/lnd:uid=1000,gid=1000 + + 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 + image: boltz/bitcoin-core:25.0 + command: + - -regtest + - -fallbackfee=0.00000253 + - -zmqpubrawtx=tcp://0.0.0.0:29000 + - -zmqpubrawblock=tcp://0.0.0.0:29001 + - -zmqpubhashblock=tcp://0.0.0.0:29002 + - -txindex + - -rpcallowip=0.0.0.0/0 + - -rpcbind=0.0.0.0 + - -addresstype=bech32 + - -changetype=bech32 + - -dbcache=2048 + - -rpcworkqueue=256 + volumes: + - bitcoin-data:/root/.bitcoin + expose: + - 29000 + - 29001 + - 29002 + - 18443 + - 18444 + healthcheck: + test: + [ + "CMD", + "bitcoin-cli", + "--rpccookiefile=/root/.bitcoin/regtest/.cookie", + "-regtest", + "getblockchaininfo", + ] + timeout: 1s + retries: 1 + interval: 1s + start_period: 0s + + clightning-1: + hostname: clightning-1 + depends_on: + - bitcoind + image: boltz/c-lightning:24.11 + command: + - --large-channels + - --network=regtest + - --grpc-port=9736 + - --bind-addr=0.0.0.0:9735 + - --bitcoin-rpcconnect=bitcoind + - --bitcoin-rpcport=18443 + - --clnrest-host=0.0.0.0 + - --clnrest-port=3010 + expose: + - 9735 + ports: + - 9736:9736 + - 3010:3010 + volumes: + - ./data/clightning-1:/root/.lightning/ + - bitcoin-data:/root/.bitcoin + + clightning-2: + hostname: clightning-2 + depends_on: + - bitcoind + image: boltz/c-lightning:22.11.1 + command: + - --large-channels + - --network=regtest + - --grpc-port=9737 + - --bind-addr=0.0.0.0:9735 + - --bitcoin-rpcconnect=bitcoind + - --bitcoin-rpcport=18443 + expose: + - 9735 + ports: + - 9737:9737 + volumes: + - ./data/clightning-2:/root/.lightning/ + - bitcoin-data:/root/.bitcoin + + clightning-2-rest: + hostname: clightning-2-rest + depends_on: + - clightning-2 + image: saubyk/c-lightning-rest:0.10.7 + entrypoint: "sh -c 'sleep 35 && /sbin/tini -g -- ./docker-entrypoint.sh'" + ports: + - 3001:3001 + expose: + - 3001 + volumes: + - ./data/clightning-2:/root/.lightning/:uid=1000,gid=1000 + - ./data/clightning-2-rest:/usr/src/app/certs/ + - bitcoin-data:/root/.bitcoin + + clightning-3: + hostname: clightning-3 + depends_on: + - bitcoind + image: boltz/c-lightning:24.11 + command: + - --large-channels + - --network=regtest + - --grpc-port=9738 + - --bind-addr=0.0.0.0:9735 + - --bitcoin-rpcconnect=bitcoind + - --bitcoin-rpcport=18443 + expose: + - 9735 + ports: + - 9738:9738 + volumes: + - ./data/clightning-3:/root/.lightning/ + - bitcoin-data:/root/.bitcoin + + lnd-1: + hostname: lnd-1 + depends_on: + - bitcoind + image: boltz/lnd:0.18.4-beta + restart: on-failure + command: + - --listen=lnd-1:9735 + - --rpclisten=lnd-1:10009 + - --restlisten=lnd-1:8081 + - --bitcoin.active + - --bitcoin.regtest + - --bitcoin.node=bitcoind + - --bitcoind.rpchost=bitcoind + - --bitcoind.rpccookie=/root/.bitcoin/regtest/.cookie + - --bitcoind.zmqpubrawtx=bitcoind:29000 + - --bitcoind.zmqpubrawblock=bitcoind:29001 + - --noseedbackup + - --protocol.wumbo-channels + expose: + - 8081 + - 9735 + - 10009 + volumes: + - ./data/lnd-1:/root/.lnd/ + - bitcoin-data:/root/.bitcoin + + lnd-2: + hostname: lnd-2 + depends_on: + - bitcoind + image: boltz/lnd:0.19.3-beta + restart: on-failure + command: + - --listen=lnd-2:9735 + - --rpclisten=lnd-2:10009 + - --restlisten=lnd-2:8081 + - --bitcoin.active + - --bitcoin.regtest + - --bitcoin.node=bitcoind + - --bitcoind.rpchost=bitcoind + - --bitcoind.rpccookie=/root/.bitcoin/regtest/.cookie + - --bitcoind.zmqpubrawtx=bitcoind:29000 + - --bitcoind.zmqpubrawblock=bitcoind:29001 + - --noseedbackup + - --protocol.wumbo-channels + expose: + - 8081 + - 9735 + - 10009 + volumes: + - ./data/lnd-2:/root/.lnd/ + - bitcoin-data:/root/.bitcoin + + lnd-3: + hostname: lnd-3 + depends_on: + - bitcoind + image: boltz/lnd:0.18.4-beta + restart: on-failure + command: + - --listen=lnd-3:9735 + - --rpclisten=lnd-3:10009 + - --restlisten=lnd-3:8081 + - --bitcoin.active + - --bitcoin.regtest + - --bitcoin.node=bitcoind + - --bitcoind.rpchost=bitcoind + - --bitcoind.rpccookie=/root/.bitcoin/regtest/.cookie + - --bitcoind.zmqpubrawtx=bitcoind:29000 + - --bitcoind.zmqpubrawblock=bitcoind:29001 + - --noseedbackup + - --protocol.wumbo-channels + ports: + - 8081:8081 + - 10009:10009 + expose: + - 8081 + - 9735 + - 10009 + volumes: + - ./data/lnd-3:/root/.lnd/ + - bitcoin-data:/root/.bitcoin + + lnd-4: + hostname: lnd-4 + depends_on: + - bitcoind + image: boltz/lnd:0.18.4-beta + restart: on-failure + command: + - --listen=lnd-4:9735 + - --rpclisten=lnd-4:10009 + - --restlisten=lnd-4:8081 + - --bitcoin.active + - --bitcoin.regtest + - --bitcoin.node=bitcoind + - --bitcoind.rpchost=bitcoind + - --bitcoind.rpccookie=/root/.bitcoin/regtest/.cookie + - --bitcoind.zmqpubrawtx=bitcoind:29000 + - --bitcoind.zmqpubrawblock=bitcoind:29001 + - --noseedbackup + - --protocol.wumbo-channels + expose: + - 8081 + - 9735 + - 10009 + volumes: + - ./data/lnd-4:/root/.lnd/ + - bitcoin-data:/root/.bitcoin + + eclair: + hostname: eclair + depends_on: + - bitcoind + image: boltz/eclair:0.8.0 + restart: on-failure + entrypoint: "sh -c 'JAVA_OPTS=-Xmx512m /eclair-node/bin/eclair-node.sh -Declair.datadir=/root/eclair -Declair.printToConsole'" + ports: + - 8082:8080 + expose: + - 9735 + - 8080 + volumes: + - ./data/eclair:/root/eclair + - bitcoin-data:/root/.bitcoin + + electrs: + hostname: electrs + restart: always + image: boltz/electrs:latest + entrypoint: ["electrs-bitcoin"] + command: + - --electrum-rpc-addr + - electrs:19001 + - --http-addr + - electrs:3002 + - --daemon-rpc-addr + - bitcoind:18443 + - --network + - regtest + - --jsonrpc-import + healthcheck: + test: ["CMD-SHELL", "curl -s $(hostname):3002/blocks/tip/height"] + timeout: 1s + retries: 20 + interval: 2s + start_period: 5s + ports: + - 19001:19001 + - 3002:3002 + volumes: + - bitcoin-data:/root/.bitcoin + + elementsd: + hostname: elementsd + restart: always + image: boltz/elements:latest + expose: + - 31001 + ports: + - 31000:31000 + - 31002:31002 + - 18884:18884 + command: + - -chain=liquidregtest + - -txindex=1 + - -rest=1 + - -server=1 + - -rpcallowip=0.0.0.0/0 + - -validatepegin=0 + - -initialfreecoins=2100000000000000 + - -fallbackfee=0.000001 + - -rpcbind=0.0.0.0 + - -rpcport=18884 + - -zmqpubrawtx=tcp://0.0.0.0:31000 + - -zmqpubrawblock=tcp://0.0.0.0:31001 + - -zmqpubhashblock=tcp://0.0.0.0:31002 + - -acceptdiscountct=1 + - -creatediscountct=1 + volumes: + - elements-data:/root/.elements + + electrs-liquid: + hostname: electrs-liquid + restart: always + image: boltz/electrs:latest + entrypoint: ["electrs-liquid"] + command: + - --electrum-rpc-addr + - electrs-liquid:19002 + - --http-addr + - electrs-liquid:3003 + - --daemon-rpc-addr + - elementsd:18884 + - --daemon-dir + - /root/.elements + - --network + - liquidregtest + - --parent-network + - regtest + - --jsonrpc-import + healthcheck: + test: ["CMD-SHELL", "curl -s $(hostname):3003/blocks/tip/height"] + timeout: 15s + retries: 20 + interval: 2s + start_period: 5s + ports: + - 19002:19002 + - 3003:3003 + volumes: + - 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/regtest/docker-scripts.sh b/docker/regtest/docker-scripts.sh new file mode 100644 index 000000000..eca62fd13 --- /dev/null +++ b/docker/regtest/docker-scripts.sh @@ -0,0 +1,322 @@ +#!/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 "$@" +} + +elements-cli-sim() { + docker exec lnbits-elementsd-1 elements-cli -rpcport=18884 -chain=liquidregtest "$@" +} + +# args(i, cmd) +lightning-cli-sim() { + i=$1 + shift # shift first argument so we can use $@ + docker exec lnbits-clightning-$i-1 lightning-cli --network regtest "$@" +} + +# args(i, cmd) +lncli-sim() { + i=$1 + shift # shift first argument so we can use $@ + docker exec lnbits-lnd-$i-1 lncli --network regtest --rpcserver=lnd-$i:10009 "$@" +} + +get-eclair-pubkey() { + while true; do + pubkey=$(docker exec lnbits-eclair-1 curl http://localhost:8080/getinfo -X POST -s -u :lnbits | jq -r .nodeId 2> /dev/null) + pubkeyPrefix=$(echo $pubkey | cut -c1,2) + if [[ "$pubkeyPrefix" == "02" || "$pubkeyPrefix" == "03" ]]; then + echo $pubkey + break + fi + sleep 1 + done +} + +wait-for-eclair-channel() { + while true; do + state=$(docker exec lnbits-eclair-1 curl http://localhost:8080/channels -X POST -s -u :lnbits | jq -r ".[0].state") + pending=$(docker exec lnbits-eclair-1 curl -s http://localhost:8080/channels -X POST -u :lnbits| jq '. | length') + echo "eclair-1 pendingchannels: $pending, current state: $state" + if [[ "$state" == "NORMAL" ]]; then + break + fi + sleep 1 + 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() { + address=$(lightning-cli-sim $1 newaddr | jq -r .bech32) + echo "funding: $address on clightning-node: $1" + bitcoin-cli-sim -named sendtoaddress address=$address amount=30 fee_rate=100 > /dev/null +} + +# args(i) +fund_lnd_node() { + address=$(lncli-sim $1 newaddress p2wkh | jq -r .address) + echo "funding: $address on lnd-node: $1" + bitcoin-cli-sim -named sendtoaddress address=$address amount=30 fee_rate=100 > /dev/null +} + +# args(i, j) +connect_clightning_node() { + pubkey=$(lightning-cli-sim $2 getinfo | jq -r '.id') + lightning-cli-sim $1 connect $pubkey@lnbits-clightning-$2-1:9735 | jq -r '.id' +} + +lnbits-regtest-start(){ + if ! command -v jq &> /dev/null + then + echo "jq is not installed" + exit + fi + if ! command -v docker &> /dev/null + then + echo "docker is not installed" + exit + fi + if ! command -v docker version &> /dev/null + then + echo "dockerd is not running" + exit + fi + lnbits-regtest-stop + docker compose up -d --remove-orphans + lnbits-regtest-init +} + +lnbits-regtest-start-log(){ + lnbits-regtest-stop + docker compose up --remove-orphans + lnbits-regtest-init +} + +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 + # 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 +} + +lnbits-regtest-restart(){ + lnbits-regtest-stop + 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 + echo "mining 150 blocks..." + bitcoin-cli-sim -generate 150 > /dev/null +} + +lnbits-elements-init(){ + echo "init_elements_wallet..." + 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(){ + echo "init_lnbits..." + docker exec lnbits-lnbits-1 uv run python tools/create_fake_admin.py +} + +lnbits-regtest-init(){ + lnbits-bitcoin-init + lnbits-elements-init + lnbits-lightning-sync + lnbits-lightning-init + boltz-client-init + lnbits-init +} + +lnbits-lightning-sync(){ + wait-for-clightning-sync 1 + wait-for-clightning-sync 2 + wait-for-clightning-sync 3 + wait-for-lnd-sync 1 + wait-for-lnd-sync 2 + wait-for-lnd-sync 3 + wait-for-lnd-sync 4 +} + +lnbits-lightning-init(){ + + # create 10 UTXOs for each node + for i in 0 1 2; do + fund_clightning_node 1 + fund_clightning_node 2 + fund_clightning_node 3 + fund_lnd_node 1 + fund_lnd_node 2 + fund_lnd_node 3 + fund_lnd_node 4 + done + + echo "mining 3 blocks..." + bitcoin-cli-sim -generate 3 > /dev/null + + lnbits-lightning-sync + + channel_confirms=6 + channel_size=24000000 # 0.024 btc + balance_size=12000000 # 0.12 btc + balance_size_msat=12000000000 # 0.12 btc + + # lnd-1 -> lnd-2 + lncli-sim 1 connect $(lncli-sim 2 getinfo | jq -r '.identity_pubkey')@lnbits-lnd-2-1 > /dev/null + echo "open channel from lnd-1 to lnd-2" + lncli-sim 1 openchannel $(lncli-sim 2 getinfo | jq -r '.identity_pubkey') $channel_size $balance_size > /dev/null + bitcoin-cli-sim -generate $channel_confirms > /dev/null + wait-for-lnd-channel 1 + + # lnd-1 -> lnd-3 + lncli-sim 1 connect $(lncli-sim 3 getinfo | jq -r '.identity_pubkey')@lnbits-lnd-3-1 > /dev/null + echo "open channel from lnd-1 to lnd-3" + lncli-sim 1 openchannel $(lncli-sim 3 getinfo | jq -r '.identity_pubkey') $channel_size $balance_size > /dev/null + bitcoin-cli-sim -generate $channel_confirms > /dev/null + wait-for-lnd-channel 1 + + # lnd-1 -> cln-1 + lncli-sim 1 connect $(lightning-cli-sim 1 getinfo | jq -r '.id')@lnbits-clightning-1-1 > /dev/null + echo "open channel from lnd-1 to cln-1" + lncli-sim 1 openchannel $(lightning-cli-sim 1 getinfo | jq -r '.id') $channel_size $balance_size > /dev/null + bitcoin-cli-sim -generate $channel_confirms > /dev/null + wait-for-lnd-channel 1 + + # lnd-1 -> cln-2 + lncli-sim 1 connect $(lightning-cli-sim 2 getinfo | jq -r '.id')@lnbits-clightning-2-1 > /dev/null + echo "open channel from lnd-1 to cln-2" + lncli-sim 1 openchannel $(lightning-cli-sim 2 getinfo | jq -r '.id') $channel_size $balance_size > /dev/null + bitcoin-cli-sim -generate $channel_confirms > /dev/null + wait-for-lnd-channel 1 + + # lnd-1 -> cln-3 + lncli-sim 1 connect $(lightning-cli-sim 3 getinfo | jq -r '.id')@lnbits-clightning-3-1 > /dev/null + echo "open channel from lnd-1 to cln-3" + lncli-sim 1 openchannel $(lightning-cli-sim 3 getinfo | jq -r '.id') $channel_size $balance_size > /dev/null + bitcoin-cli-sim -generate $channel_confirms > /dev/null + wait-for-lnd-channel 1 + + # lnd-2 -> cln-2 + lncli-sim 2 connect $(lightning-cli-sim 2 getinfo | jq -r '.id')@lnbits-clightning-2-1 > /dev/null + echo "open channel from lnd-2 to cln-2" + lncli-sim 2 openchannel $(lightning-cli-sim 2 getinfo | jq -r '.id') $channel_size $balance_size > /dev/null + bitcoin-cli-sim -generate $channel_confirms > /dev/null + wait-for-lnd-channel 2 + + # lnd-3 -> cln-3 + lncli-sim 3 connect $(lightning-cli-sim 3 getinfo | jq -r '.id')@lnbits-clightning-3-1 > /dev/null + echo "open channel from lnd-3 to cln-1" + lncli-sim 3 openchannel $(lightning-cli-sim 3 getinfo | jq -r '.id') $channel_size $balance_size > /dev/null + bitcoin-cli-sim -generate $channel_confirms > /dev/null + wait-for-lnd-channel 3 + + # lnd-3 -> cln-1 + lncli-sim 3 connect $(lightning-cli-sim 1 getinfo | jq -r '.id')@lnbits-clightning-1-1 > /dev/null + echo "open channel from lnd-3 to cln-1" + lncli-sim 3 openchannel $(lightning-cli-sim 1 getinfo | jq -r '.id') $channel_size $balance_size > /dev/null + bitcoin-cli-sim -generate $channel_confirms > /dev/null + wait-for-lnd-channel 3 + + # lnd-1 -> eclair-1 + lncli-sim 1 connect $(get-eclair-pubkey)@lnbits-eclair-1 > /dev/null + echo "open channel from lnd-2 to eclair-1" + lncli-sim 1 openchannel $(get-eclair-pubkey) $channel_size $balance_size > /dev/null + bitcoin-cli-sim -generate $channel_confirms > /dev/null + wait-for-lnd-channel 1 + + # lnd-2 -> eclair-1 + lncli-sim 2 connect $(get-eclair-pubkey)@lnbits-eclair-1 > /dev/null + echo "open channel from lnd-2 to eclair-1" + lncli-sim 2 openchannel $(get-eclair-pubkey) $channel_size $balance_size > /dev/null + bitcoin-cli-sim -generate $channel_confirms > /dev/null + wait-for-lnd-channel 2 + + wait-for-clightning-channel 1 + wait-for-clightning-channel 2 + wait-for-clightning-channel 3 + + wait-for-eclair-channel + + lnbits-lightning-sync + +} + +wait-for-lnd-channel(){ + while true; do + pending=$(lncli-sim $1 pendingchannels | jq -r '.pending_open_channels | length') + echo "lnd-$1 pendingchannels: $pending" + if [[ "$pending" == "0" ]]; then + break + fi + sleep 1 + done +} + +wait-for-lnd-sync(){ + while true; do + if [[ "$(lncli-sim $1 getinfo 2>&1 | jq -r '.synced_to_chain' 2> /dev/null)" == "true" ]]; then + echo "lnd-$1 is synced!" + break + fi + echo "waiting for lnd-$1 to sync..." + sleep 1 + done +} + +wait-for-clightning-channel(){ + while true; do + pending=$(lightning-cli-sim $1 getinfo | jq -r '.num_pending_channels | length') + echo "cln-$1 pendingchannels: $pending" + if [[ "$pending" == "0" ]]; then + if [[ "$(lightning-cli-sim $1 getinfo 2>&1 | jq -r '.warning_bitcoind_sync' 2> /dev/null)" == "null" ]]; then + if [[ "$(lightning-cli-sim $1 getinfo 2>&1 | jq -r '.warning_lightningd_sync' 2> /dev/null)" == "null" ]]; then + break + fi + fi + fi + sleep 1 + done +} + +wait-for-clightning-sync(){ + while true; do + if [[ ! "$(lightning-cli-sim $1 getinfo 2>&1 | jq -r '.id' 2> /dev/null)" == "null" ]]; then + if [[ "$(lightning-cli-sim $1 getinfo 2>&1 | jq -r '.warning_bitcoind_sync' 2> /dev/null)" == "null" ]]; then + if [[ "$(lightning-cli-sim $1 getinfo 2>&1 | jq -r '.warning_lightningd_sync' 2> /dev/null)" == "null" ]]; then + echo "cln-$1 is synced!" + break + fi + fi + fi + echo "waiting for cln-$1 to sync..." + sleep 1 + done +} diff --git a/docker/regtest/start-regtest b/docker/regtest/start-regtest new file mode 100755 index 000000000..82a5ad6ee --- /dev/null +++ b/docker/regtest/start-regtest @@ -0,0 +1,74 @@ +#!/bin/bash +print_success() { + printf "\033[;1;32mPASSED\033[;0m $1\n" +} + +print_error() { + printf "\033[;1;31mFAILED\033[;0m $1\n" +} + +run(){ + label=$1 + value=$2 + cmd=$3 + if [[ "$cmd" == "$value" ]]; then + print_success "$label is $cmd" + else + print_error "$label is $cmd, should be $value" + failed="true" + fi +} + +failed="false" +blockheight=213 +utxos=3 +channel_size=24000000 # 0.024 btc +balance_size=12000000 # 0.012 btc + +source $(pwd)/docker-scripts.sh +lnbits-regtest-start +echo "==================================" +printf "\033[;1;36mregtest started! starting tests...\033[;0m\n" +echo "==================================" +echo "" + +for i in 1 2 3; do + run "lnd-$i .synced_to_chain" "true" $(lncli-sim $i getinfo | jq -r ".synced_to_chain") + run "lnd-$i utxo count" $utxos $(lncli-sim $i listunspent | jq -r ".utxos | length") + run "lnd-$i .block_height" $blockheight $(lncli-sim $i getinfo | jq -r ".block_height") + if [[ "$i" == "1" ]]; then + channel_count=6 + else + channel_count=3 + fi + run "lnd-$i openchannels" $channel_count $(lncli-sim $i listchannels | jq -r ".channels | length") + run "lnd-$i .channels[0].capacity" $channel_size $(lncli-sim $i listchannels | jq -r ".channels[0].capacity") + run "lnd-$i .channels[0].push_amount_sat" $balance_size $(lncli-sim $i listchannels | jq -r ".channels[0].push_amount_sat") +done +for i in 1 2 3; do + # run "cln-$i blockheight" $blockheight $(lightning-cli-sim $i getinfo | jq -r ".blockheight") + run "cln-$i utxo count" $utxos $(lightning-cli-sim $i listfunds | jq -r ".outputs | length") + run "cln-$i openchannels" 2 $(lightning-cli-sim $i getinfo | jq -r ".num_active_channels") + run "cln-$i channel[0].state" "CHANNELD_NORMAL" $(lightning-cli-sim $i listfunds | jq -r ".channels[0].state") + run "cln-$i channel[0].amount_msat" $(($channel_size * 1000)) $(lightning-cli-sim $i listfunds | jq -r ".channels[0].amount_msat" | sed 's/msat//g') + run "cln-$i channel[0].our_amount_msat" $(($balance_size * 1000)) $(lightning-cli-sim $i listfunds | jq -r ".channels[0].our_amount_msat" | sed 's/msat//g') +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 + echo "" + echo "==================================" + print_error "one more more tests failed" + echo "==================================" + exit 1 +else + echo "" + echo "==================================" + print_success "all tests passed! yay!" + echo "==================================" +fi diff --git a/docker/sparkl2/Dockerfile b/docker/sparkl2/Dockerfile new file mode 100644 index 000000000..7bf785c43 --- /dev/null +++ b/docker/sparkl2/Dockerfile @@ -0,0 +1,36 @@ +ARG LNBITS_TAG=latest + +FROM lnbits/lnbits:${LNBITS_TAG} + +RUN apt-get update && apt-get -y upgrade +RUN apt-get install -y netcat-openbsd npm nodejs git + +RUN curl -LsSf https://astral.sh/uv/install.sh | sh +ENV PATH="/root/.local/bin:$PATH" + +# install sparksidecar +RUN git clone https://github.com/lnbits/spark_sidecar +RUN cd spark_sidecar && npm install + +# Reinstall dependencies for lnbits just in case (needed for CMD usage) +RUN uv sync --all-extras + +# LNBITS +ENV LNBITS_PORT="5000" +ENV LNBITS_HOST="0.0.0.0" +ENV LNBITS_BACKEND_WALLET_CLASS="SparkL2Wallet" +ENV FUNDING_SOURCE_MAX_RETRIES=10 + +# spark sidecar +ENV SPARK_MNEMONIC="bottom bottom bottom bottom bottom bottom bottom bottom bottom bottom bottom bottom" +ENV SPARK_NETWORK="MAINNET" +ENV SPARK_SIDECAR_PORT="8765" +ENV SPARK_PAY_WAIT_MS="20000" + +EXPOSE 5000 + +COPY entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh + +# Entrypoint to start sparksidebar and LNbits +CMD ["/entrypoint.sh"] diff --git a/docker/sparkl2/entrypoint.sh b/docker/sparkl2/entrypoint.sh new file mode 100644 index 000000000..f13c3b95e --- /dev/null +++ b/docker/sparkl2/entrypoint.sh @@ -0,0 +1,25 @@ +#!/bin/bash +set -e + +node spark_sidecar/server.mjs & + +SIDECAR_PID=$! + +# Wait for startup +for i in {1..10}; do + if nc -z localhost $SPARK_SIDECAR_PORT; then + echo "sparksidebar is up!" + break + fi + echo "Waiting for sparksidebar to start..." + sleep 1 +done + +# Optional: check if still not up +if ! nc -z localhost $SPARK_SIDECAR_PORT; then + echo "sparksidebar did not start successfully." + exit 1 +fi + +echo "Starting LNbits on $LNBITS_HOST:$LNBITS_PORT..." +exec uv run lnbits --port "$LNBITS_PORT" --host "$LNBITS_HOST" --forwarded-allow-ips='*'