From a6773abacad70f779d1e118bdfa76bb6985f07b6 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 3 Apr 2026 13:53:07 +0100 Subject: [PATCH] fix(pos): add --rpc-ignore-offline to all daemon configs PoS mining requires this flag when the daemon has no peers. Without it, the daemon's get_pos_mining_details RPC returns DISCONNECTED status and the wallet refuses to mint. First PoS blocks minted on testnet at height 12,382. Co-Authored-By: Charon --- docker/.env | 54 ++++++ docker/Dockerfile.cross-build | 38 +++++ docker/Dockerfile.staking-node | 1 + docker/build-all-platforms.sh | 77 +++++++++ docker/docker-compose.exit.yml | 1 + docker/docker-compose.node.yml | 1 + docker/docker-compose.nodes.yml | 114 +++++++++++++ docker/docker-compose.pull.yml | 1 + docker/docker-compose.testnet.yml | 221 +++++++++++++++++++++++++ docker/docker-compose.vpn.yml | 126 ++++++++++++++ docker/lethean/.env | 52 ++++++ docker/lethean/.env.example | 52 ++++++ docker/lethean/docker-compose.node.yml | 86 ++++++++++ docker/test-network.sh | 90 ++++++++++ 14 files changed, 914 insertions(+) create mode 100644 docker/.env create mode 100644 docker/Dockerfile.cross-build create mode 100755 docker/build-all-platforms.sh create mode 100644 docker/docker-compose.nodes.yml create mode 100644 docker/docker-compose.testnet.yml create mode 100644 docker/docker-compose.vpn.yml create mode 100644 docker/lethean/.env create mode 100644 docker/lethean/.env.example create mode 100644 docker/lethean/docker-compose.node.yml create mode 100755 docker/test-network.sh diff --git a/docker/.env b/docker/.env new file mode 100644 index 00000000..5ed6f23b --- /dev/null +++ b/docker/.env @@ -0,0 +1,54 @@ +# Lethean Testnet Configuration +# Copy to .env and customise before running: +# cp .env.example .env +# docker compose -f docker-compose.pull.yml up -d + +# Public hostname (used by explorer and trade frontend) +PUBLIC_HOST=localhost + +# Wallet password (empty = no password, change for production) +WALLET_PASSWORD= + +# Trade API JWT secret (CHANGE THIS for production) +JWT_SECRET=change-me-before-production + +# Explorer database +EXPLORER_DB_USER=explorer +EXPLORER_DB_PASS=explorer +EXPLORER_DB_NAME=lethean_explorer + +# Trade database +TRADE_DB_USER=trade +TRADE_DB_PASS=trade +TRADE_DB_NAME=lethean_trade + +# Daemon log level (0=minimal, 1=normal, 2=detailed, 3=trace) +DAEMON_LOG_LEVEL=1 + +# Port overrides (defaults shown) +# DAEMON_RPC_PORT=46941 +# DAEMON_P2P_PORT=46942 +# WALLET_RPC_PORT=46944 +# EXPLORER_PORT=3335 +# TRADE_API_PORT=3336 +# TRADE_FRONTEND_PORT=3338 +# POOL_STRATUM_PORT=5555 +# POOL_API_PORT=2117 +# LNS_HTTP_PORT=5553 +# LNS_DNS_PORT=5354 + +# === Exit Node Settings (docker-compose.exit.yml) === + +# Your public IP address (required for VPN exit node) +# EXIT_PUBLIC_IP=auto + +# Exit node name (registered as on-chain alias) +# EXIT_NAME=my-exit-node + +# Maximum VPN peers (WireGuard clients) +# EXIT_MAX_PEERS=25 + +# Timezone +# TZ=Europe/London +EXIT_PUBLIC_IP=203.0.113.50 +EXIT_NAME=my-exit diff --git a/docker/Dockerfile.cross-build b/docker/Dockerfile.cross-build new file mode 100644 index 00000000..f674ef28 --- /dev/null +++ b/docker/Dockerfile.cross-build @@ -0,0 +1,38 @@ +# Cross-platform build for Lethean blockchain binaries +# Produces Linux x86_64 and ARM64 binaries +# macOS and Windows need native build environments or cross-compilation toolchains +# +# Usage: +# docker build -f Dockerfile.cross-build --target linux-x64 -o ./out . +# docker build -f Dockerfile.cross-build --target linux-arm64 -o ./out . + +# === Base builder === +FROM ubuntu:24.04 AS base-builder +ENV DEBIAN_FRONTEND=noninteractive +RUN apt-get update && apt-get install -y \ + build-essential cmake git python3 python3-pip \ + libboost-all-dev libssl-dev pkg-config curl \ + && pip3 install conan --break-system-packages \ + && rm -rf /var/lib/apt/lists/* +WORKDIR /build +COPY . /build/ + +# === Linux x86_64 === +FROM base-builder AS linux-x64-build +ARG TESTNET=1 +RUN make build CPU_CORES=$(nproc) TESTNET=${TESTNET} STATIC=1 || \ + (cd build/release && cmake --build . --parallel $(nproc)) + +FROM scratch AS linux-x64 +COPY --from=linux-x64-build /build/build/release/src/lethean-* / + +# === Linux ARM64 (cross-compile) === +FROM base-builder AS linux-arm64-build +RUN apt-get update && apt-get install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu +ARG TESTNET=1 +# ARM64 cross-compilation requires CMAKE_TOOLCHAIN_FILE +# This is a placeholder — actual cross-compile needs Conan profile for aarch64 +RUN echo "ARM64 cross-compilation requires additional setup. Use native ARM64 builder or QEMU." + +FROM scratch AS linux-arm64 +# Placeholder — use buildx with --platform=linux/arm64 for native ARM builds diff --git a/docker/Dockerfile.staking-node b/docker/Dockerfile.staking-node index 0a05932d..da89a5a6 100644 --- a/docker/Dockerfile.staking-node +++ b/docker/Dockerfile.staking-node @@ -52,6 +52,7 @@ command=lethean-chain-node --allow-local-ip --log-level %(ENV_DAEMON_LOG_LEVEL)s --disable-upnp + --rpc-ignore-offline autorestart=true stdout_logfile=/dev/stdout stdout_logfile_maxbytes=0 diff --git a/docker/build-all-platforms.sh b/docker/build-all-platforms.sh new file mode 100755 index 00000000..311418c5 --- /dev/null +++ b/docker/build-all-platforms.sh @@ -0,0 +1,77 @@ +#!/bin/bash +# Build Lethean binaries for all platforms +# Requires: Docker with buildx for multi-arch +# +# Usage: +# ./build-all-platforms.sh testnet # testnet binaries +# ./build-all-platforms.sh mainnet # mainnet binaries +# +# Outputs to: ../build/packages/ + +set -e + +TARGET=${1:-testnet} +TESTNET=$( [ "$TARGET" = "testnet" ] && echo 1 || echo 0 ) +VERSION=$(grep 'BUILD_VERSION:=' ../Makefile | cut -d= -f2) +OUTDIR="../build/packages" + +echo "Building Lethean $TARGET v$VERSION for all platforms" +mkdir -p "$OUTDIR" + +# === Linux x86_64 (Docker) === +echo "" +echo "=== Linux x86_64 ===" +docker build -f Dockerfile.cross-build \ + --build-arg TESTNET=$TESTNET \ + --target linux-x64 \ + -o "$OUTDIR/linux-x64" \ + .. 2>&1 | tail -5 + +if ls "$OUTDIR/linux-x64/lethean-"* > /dev/null 2>&1; then + PKG="lethean-${TARGET}-linux-x86_64-v${VERSION}" + mkdir -p "$OUTDIR/$PKG" + cp "$OUTDIR/linux-x64/lethean-"* "$OUTDIR/$PKG/" + cd "$OUTDIR" && tar czf "${PKG}.tar.gz" "$PKG/" && cd - + echo " Package: $OUTDIR/${PKG}.tar.gz" +else + echo " Build failed — check Docker output" +fi + +# === Linux ARM64 (needs Docker buildx with QEMU) === +echo "" +echo "=== Linux ARM64 ===" +if docker buildx ls 2>/dev/null | grep -q "linux/arm64"; then + echo " ARM64 builder available — building..." + docker buildx build -f ../utils/docker/lthn-chain/Dockerfile \ + --platform linux/arm64 \ + --build-arg BUILD_TESTNET=$TESTNET \ + --build-arg BUILD_THREADS=4 \ + --target build-artifacts \ + -o "$OUTDIR/linux-arm64" \ + .. 2>&1 | tail -5 +else + echo " SKIP: Docker buildx ARM64 emulation not configured" + echo " To enable: docker run --privileged --rm tonistiigi/binfmt --install arm64" +fi + +# === macOS (needs native build or osxcross) === +echo "" +echo "=== macOS ===" +echo " SKIP: macOS builds require native macOS environment or osxcross toolchain" +echo " Build on Cladius (M3 Ultra): cd blockchain && make $TARGET" +echo " Or use GitHub Actions with macos-latest runner" + +# === Windows (needs MSVC or mingw-w64) === +echo "" +echo "=== Windows ===" +echo " SKIP: Windows builds require MSVC or mingw-w64 cross-compiler" +echo " Recommended: GitHub Actions with windows-latest runner" +echo " Or: Docker with dockcross/windows-shared-x64 image" + +# === Summary === +echo "" +echo "=== Build Summary ===" +ls -lh "$OUTDIR"/*.tar.gz 2>/dev/null || echo "No packages built yet" +echo "" +echo "For macOS/Windows, use CI/CD:" +echo " .forgejo/workflows/build-release.yml (push a tag to trigger)" diff --git a/docker/docker-compose.exit.yml b/docker/docker-compose.exit.yml index b5962c6c..291a8467 100644 --- a/docker/docker-compose.exit.yml +++ b/docker/docker-compose.exit.yml @@ -51,6 +51,7 @@ services: - --log-level - "${DAEMON_LOG_LEVEL:-1}" - --disable-upnp + - --rpc-ignore-offline healthcheck: test: ["CMD-SHELL", "curl -sf http://localhost:36941/json_rpc -d '{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"getinfo\"}' -H 'Content-Type: application/json' | grep -q OK"] interval: 30s diff --git a/docker/docker-compose.node.yml b/docker/docker-compose.node.yml index a6613a21..826c401a 100644 --- a/docker/docker-compose.node.yml +++ b/docker/docker-compose.node.yml @@ -47,6 +47,7 @@ services: - --log-level - "${DAEMON_LOG_LEVEL:-1}" - --disable-upnp + - --rpc-ignore-offline healthcheck: test: ["CMD-SHELL", "curl -sf http://localhost:36941/json_rpc -d '{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"getinfo\"}' -H 'Content-Type: application/json' | grep -q OK"] interval: 30s diff --git a/docker/docker-compose.nodes.yml b/docker/docker-compose.nodes.yml new file mode 100644 index 00000000..4c4a0788 --- /dev/null +++ b/docker/docker-compose.nodes.yml @@ -0,0 +1,114 @@ +# Lethean Chain Nodes Only — minimal network simulation +# 3 nodes + miner, no explorer/trade (for testing consensus, HF activation, reorgs) +# +# Usage: +# docker compose -f docker-compose.nodes.yml up -d +# # Start mining on node1: +# docker exec lthn-node1 curl -X POST http://127.0.0.1:36941/start_mining \ +# -H 'Content-Type: application/json' \ +# -d '{"miner_address":"YOUR_iTHN_ADDRESS","threads_count":2}' +# # Check sync status: +# for n in 1 2 3; do +# echo -n "node$n: "; docker exec lthn-node$n curl -s http://127.0.0.1:36941/json_rpc \ +# -H 'Content-Type: application/json' \ +# -d '{"jsonrpc":"2.0","id":"0","method":"getinfo"}' | python3 -c "import sys,json; print(json.load(sys.stdin)['result']['height'])" +# done + +services: + node1: + build: + context: .. + dockerfile: utils/docker/lthn-chain/Dockerfile + target: chain-service + args: + BUILD_TESTNET: 1 + BUILD_THREADS: 4 + container_name: lthn-node1 + ports: + - "46941:36941" + - "46942:36942" + volumes: + - node1-data:/data + command: > + lethean-chain-node + --data-dir /data + --rpc-bind-ip 0.0.0.0 + --rpc-bind-port 36941 + --p2p-bind-port 36942 + --rpc-enable-admin-api + --allow-local-ip + --log-level 1 + --disable-upnp + networks: + lthn-net: + ipv4_address: 172.29.0.10 + + node2: + build: + context: .. + dockerfile: utils/docker/lthn-chain/Dockerfile + target: chain-service + args: + BUILD_TESTNET: 1 + BUILD_THREADS: 4 + container_name: lthn-node2 + ports: + - "46951:36941" + volumes: + - node2-data:/data + command: > + lethean-chain-node + --data-dir /data + --rpc-bind-ip 0.0.0.0 + --rpc-bind-port 36941 + --p2p-bind-port 36942 + --add-exclusive-node 172.29.0.10:36942 + --allow-local-ip + --log-level 1 + --disable-upnp + depends_on: + - node1 + networks: + lthn-net: + ipv4_address: 172.29.0.11 + + node3: + build: + context: .. + dockerfile: utils/docker/lthn-chain/Dockerfile + target: chain-service + args: + BUILD_TESTNET: 1 + BUILD_THREADS: 4 + container_name: lthn-node3 + ports: + - "46961:36941" + volumes: + - node3-data:/data + command: > + lethean-chain-node + --data-dir /data + --rpc-bind-ip 0.0.0.0 + --rpc-bind-port 36941 + --p2p-bind-port 36942 + --add-exclusive-node 172.29.0.10:36942 + --allow-local-ip + --log-level 1 + --disable-upnp + depends_on: + - node1 + networks: + lthn-net: + ipv4_address: 172.29.0.12 + +networks: + lthn-net: + driver: bridge + ipam: + config: + - subnet: 172.29.0.0/16 + +volumes: + node1-data: + node2-data: + node3-data: diff --git a/docker/docker-compose.pull.yml b/docker/docker-compose.pull.yml index c0163a76..55ac0a0d 100644 --- a/docker/docker-compose.pull.yml +++ b/docker/docker-compose.pull.yml @@ -43,6 +43,7 @@ services: - --log-level - "${DAEMON_LOG_LEVEL:-1}" - --disable-upnp + - --rpc-ignore-offline healthcheck: test: ["CMD-SHELL", "curl -sf http://localhost:36941/json_rpc -d '{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"getinfo\"}' -H 'Content-Type: application/json' | grep -q OK"] interval: 30s diff --git a/docker/docker-compose.testnet.yml b/docker/docker-compose.testnet.yml new file mode 100644 index 00000000..fddffbcd --- /dev/null +++ b/docker/docker-compose.testnet.yml @@ -0,0 +1,221 @@ +# Lethean Testnet Network Simulation +# Spins up a 3-node testnet with explorer, wallet RPC, and trade services +# +# Usage: +# docker compose -f docker-compose.testnet.yml up -d +# docker compose -f docker-compose.testnet.yml logs -f node1 +# docker compose -f docker-compose.testnet.yml down -v +# +# Nodes discover each other via exclusive-node flags. +# Node1 is the seed node, Node2 and Node3 connect to it. + +services: + # === Blockchain Nodes === + + node1: + build: + context: .. + dockerfile: utils/docker/lthn-chain/Dockerfile + target: chain-service + args: + BUILD_TESTNET: 1 + BUILD_THREADS: 4 + container_name: lthn-node1 + ports: + - "46941:36941" # RPC + - "46942:36942" # P2P + volumes: + - node1-data:/data + command: > + lethean-chain-node + --data-dir /data + --rpc-bind-ip 0.0.0.0 + --rpc-bind-port 36941 + --p2p-bind-port 36942 + --rpc-enable-admin-api + --allow-local-ip + --log-level 1 + --disable-upnp + networks: + lthn-testnet: + ipv4_address: 172.28.0.10 + + node2: + build: + context: .. + dockerfile: utils/docker/lthn-chain/Dockerfile + target: chain-service + args: + BUILD_TESTNET: 1 + BUILD_THREADS: 4 + container_name: lthn-node2 + volumes: + - node2-data:/data + command: > + lethean-chain-node + --data-dir /data + --rpc-bind-ip 0.0.0.0 + --rpc-bind-port 36941 + --p2p-bind-port 36942 + --add-exclusive-node 172.28.0.10:36942 + --allow-local-ip + --log-level 1 + --disable-upnp + depends_on: + - node1 + networks: + lthn-testnet: + ipv4_address: 172.28.0.11 + + node3: + build: + context: .. + dockerfile: utils/docker/lthn-chain/Dockerfile + target: chain-service + args: + BUILD_TESTNET: 1 + BUILD_THREADS: 4 + container_name: lthn-node3 + volumes: + - node3-data:/data + command: > + lethean-chain-node + --data-dir /data + --rpc-bind-ip 0.0.0.0 + --rpc-bind-port 36941 + --p2p-bind-port 36942 + --add-exclusive-node 172.28.0.10:36942 + --allow-local-ip + --log-level 1 + --disable-upnp + depends_on: + - node1 + networks: + lthn-testnet: + ipv4_address: 172.28.0.12 + + # === Wallet RPC === + + wallet: + build: + context: .. + dockerfile: utils/docker/lthn-chain/Dockerfile + target: chain-service + args: + BUILD_TESTNET: 1 + BUILD_THREADS: 4 + container_name: lthn-wallet + ports: + - "46944:36944" # Wallet RPC + volumes: + - wallet-data:/wallet + entrypoint: > + sh -c " + if [ ! -f /wallet/testnet.wallet ]; then + echo '' | lethean-wallet-cli --generate-new-wallet /wallet/testnet.wallet --password '' --daemon-address node1:36941 --command exit; + fi; + lethean-wallet-cli + --wallet-file /wallet/testnet.wallet + --password '' + --daemon-address node1:36941 + --rpc-bind-port 36944 + --rpc-bind-ip 0.0.0.0 + --do-pos-mining + " + depends_on: + - node1 + networks: + lthn-testnet: + ipv4_address: 172.28.0.20 + + # === Explorer === + + explorer-db: + image: postgres:16-alpine + container_name: lthn-explorer-db + environment: + POSTGRES_USER: explorer + POSTGRES_PASSWORD: explorer + POSTGRES_DB: lethean_explorer + volumes: + - explorer-db-data:/var/lib/postgresql/data + networks: + lthn-testnet: + ipv4_address: 172.28.0.30 + + explorer: + build: + context: ../../lthn/zano-upstream/zano-explorer-zarcanum + dockerfile: Dockerfile + container_name: lthn-explorer + ports: + - "3335:3335" + environment: + API: http://node1:36941 + FRONTEND_API: http://127.0.0.1:3335 + SERVER_PORT: "3335" + AUDITABLE_WALLET_API: http://node1:36941 + PGUSER: explorer + PGPASSWORD: explorer + PGDATABASE: lethean_explorer + PGHOST: explorer-db + PGPORT: "5432" + MEXC_API_URL: "" + depends_on: + - node1 + - explorer-db + networks: + lthn-testnet: + ipv4_address: 172.28.0.31 + + # === Trade Backend === + + trade-db: + image: postgres:16-alpine + container_name: lthn-trade-db + environment: + POSTGRES_USER: trade + POSTGRES_PASSWORD: trade + POSTGRES_DB: lethean_trade + volumes: + - trade-db-data:/var/lib/postgresql/data + networks: + lthn-testnet: + ipv4_address: 172.28.0.40 + + trade-api: + build: + context: ../../lthn/zano-upstream/zano_trade_backend + container_name: lthn-trade-api + ports: + - "3336:3336" + environment: + PORT: "3336" + PGUSER: trade + PGPASSWORD: trade + PGDATABASE: lethean_trade + PGHOST: trade-db + PGPORT: "5432" + JWT_SECRET: testnet-dev-secret + DAEMON_RPC_URL: http://node1:36941/json_rpc + depends_on: + - node1 + - trade-db + networks: + lthn-testnet: + ipv4_address: 172.28.0.41 + +networks: + lthn-testnet: + driver: bridge + ipam: + config: + - subnet: 172.28.0.0/16 + +volumes: + node1-data: + node2-data: + node3-data: + wallet-data: + explorer-db-data: + trade-db-data: diff --git a/docker/docker-compose.vpn.yml b/docker/docker-compose.vpn.yml new file mode 100644 index 00000000..0a94e413 --- /dev/null +++ b/docker/docker-compose.vpn.yml @@ -0,0 +1,126 @@ +# Lethean VPN Stack — sandboxed legacy Python + WireGuard +# Chain node + wallet + VPN dispatcher + WireGuard gateway +# +# Usage: +# docker compose -f docker-compose.vpn.yml up -d +# +# This sandboxes the legacy Python VPN code inside containers +# until the CoreGO replacement is ready. + +services: + # Chain daemon (testnet) + daemon: + build: + context: .. + dockerfile: utils/docker/lthn-chain/Dockerfile + target: chain-service + args: + BUILD_TESTNET: 1 + BUILD_THREADS: 4 + container_name: lthn-vpn-daemon + volumes: + - daemon-data:/data + command: > + lethean-chain-node + --data-dir /data + --rpc-bind-ip 0.0.0.0 + --rpc-bind-port 36941 + --p2p-bind-port 36942 + --rpc-enable-admin-api + --allow-local-ip + --log-level 1 + --disable-upnp + networks: + vpn-net: + ipv4_address: 172.31.0.10 + + # Wallet RPC (for payment processing) + wallet: + build: + context: .. + dockerfile: utils/docker/lthn-chain/Dockerfile + target: chain-service + args: + BUILD_TESTNET: 1 + BUILD_THREADS: 4 + container_name: lthn-vpn-wallet + volumes: + - wallet-data:/wallet + entrypoint: > + sh -c " + if [ ! -f /wallet/vpn.wallet ]; then + echo '' | lethean-wallet-cli --generate-new-wallet /wallet/vpn.wallet --password '' --daemon-address daemon:36941 --command exit; + fi; + lethean-wallet-cli + --wallet-file /wallet/vpn.wallet + --password '' + --daemon-address daemon:36941 + --rpc-bind-port 36944 + --rpc-bind-ip 0.0.0.0 + " + depends_on: + - daemon + networks: + vpn-net: + ipv4_address: 172.31.0.20 + + # VPN Dispatcher (legacy Python, sandboxed) + dispatcher: + build: + context: ../../lthn/lthn-app-vpn + container_name: lthn-vpn-dispatcher + cap_add: + - NET_ADMIN + environment: + DAEMON_HOST: daemon + DAEMON_RPC_PORT: "36941" + MODE: server + ports: + - "8124:8124" # Server management API + depends_on: + - daemon + - wallet + networks: + vpn-net: + ipv4_address: 172.31.0.30 + + # WireGuard Gateway + wireguard: + image: lscr.io/linuxserver/wireguard:latest + container_name: lthn-vpn-wireguard + cap_add: + - NET_ADMIN + - SYS_MODULE + environment: + PUID: 1000 + PGID: 1000 + TZ: Europe/London + SERVERURL: auto + SERVERPORT: 51820 + PEERS: 10 + PEERDNS: 1.1.1.1 + INTERNAL_SUBNET: 10.13.13.0 + ALLOWEDIPS: 0.0.0.0/0,::/0 + LOG_CONFS: "false" + ports: + - "51820:51820/udp" + volumes: + - wireguard-config:/config + sysctls: + - net.ipv4.conf.all.src_valid_mark=1 + - net.ipv4.ip_forward=1 + networks: + vpn-net: + ipv4_address: 172.31.0.40 + +networks: + vpn-net: + driver: bridge + ipam: + config: + - subnet: 172.31.0.0/24 + +volumes: + daemon-data: + wallet-data: + wireguard-config: diff --git a/docker/lethean/.env b/docker/lethean/.env new file mode 100644 index 00000000..fb9311d9 --- /dev/null +++ b/docker/lethean/.env @@ -0,0 +1,52 @@ +# Lethean Testnet Configuration +# Copy to .env and customise before running: +# cp .env.example .env +# docker compose -f docker-compose.pull.yml up -d + +# Public hostname (used by explorer and trade frontend) +PUBLIC_HOST=localhost + +# Wallet password (empty = no password, change for production) +WALLET_PASSWORD=my-secure-pass + +# Trade API JWT secret (CHANGE THIS for production) +JWT_SECRET=change-me-before-production + +# Explorer database +EXPLORER_DB_USER=explorer +EXPLORER_DB_PASS=explorer +EXPLORER_DB_NAME=lethean_explorer + +# Trade database +TRADE_DB_USER=trade +TRADE_DB_PASS=trade +TRADE_DB_NAME=lethean_trade + +# Daemon log level (0=minimal, 1=normal, 2=detailed, 3=trace) +DAEMON_LOG_LEVEL=1 + +# Port overrides (defaults shown) +# DAEMON_RPC_PORT=46941 +# DAEMON_P2P_PORT=46942 +# WALLET_RPC_PORT=46944 +# EXPLORER_PORT=3335 +# TRADE_API_PORT=3336 +# TRADE_FRONTEND_PORT=3338 +# POOL_STRATUM_PORT=5555 +# POOL_API_PORT=2117 +# LNS_HTTP_PORT=5553 +# LNS_DNS_PORT=5354 + +# === Exit Node Settings (docker-compose.exit.yml) === + +# Your public IP address (required for VPN exit node) +# EXIT_PUBLIC_IP=auto + +# Exit node name (registered as on-chain alias) +# EXIT_NAME=my-exit-node + +# Maximum VPN peers (WireGuard clients) +# EXIT_MAX_PEERS=25 + +# Timezone +# TZ=Europe/London diff --git a/docker/lethean/.env.example b/docker/lethean/.env.example new file mode 100644 index 00000000..1bb75153 --- /dev/null +++ b/docker/lethean/.env.example @@ -0,0 +1,52 @@ +# Lethean Testnet Configuration +# Copy to .env and customise before running: +# cp .env.example .env +# docker compose -f docker-compose.pull.yml up -d + +# Public hostname (used by explorer and trade frontend) +PUBLIC_HOST=localhost + +# Wallet password (empty = no password, change for production) +WALLET_PASSWORD= + +# Trade API JWT secret (CHANGE THIS for production) +JWT_SECRET=change-me-before-production + +# Explorer database +EXPLORER_DB_USER=explorer +EXPLORER_DB_PASS=explorer +EXPLORER_DB_NAME=lethean_explorer + +# Trade database +TRADE_DB_USER=trade +TRADE_DB_PASS=trade +TRADE_DB_NAME=lethean_trade + +# Daemon log level (0=minimal, 1=normal, 2=detailed, 3=trace) +DAEMON_LOG_LEVEL=1 + +# Port overrides (defaults shown) +# DAEMON_RPC_PORT=46941 +# DAEMON_P2P_PORT=46942 +# WALLET_RPC_PORT=46944 +# EXPLORER_PORT=3335 +# TRADE_API_PORT=3336 +# TRADE_FRONTEND_PORT=3338 +# POOL_STRATUM_PORT=5555 +# POOL_API_PORT=2117 +# LNS_HTTP_PORT=5553 +# LNS_DNS_PORT=5354 + +# === Exit Node Settings (docker-compose.exit.yml) === + +# Your public IP address (required for VPN exit node) +# EXIT_PUBLIC_IP=auto + +# Exit node name (registered as on-chain alias) +# EXIT_NAME=my-exit-node + +# Maximum VPN peers (WireGuard clients) +# EXIT_MAX_PEERS=25 + +# Timezone +# TZ=Europe/London diff --git a/docker/lethean/docker-compose.node.yml b/docker/lethean/docker-compose.node.yml new file mode 100644 index 00000000..a6613a21 --- /dev/null +++ b/docker/lethean/docker-compose.node.yml @@ -0,0 +1,86 @@ +# Lethean Home Node +# A self-contained chain node for home users who want to support the network. +# Syncs the chain, runs a wallet with PoS staking, and exposes P2P for peering. +# +# Usage: +# cp .env.example .env # edit WALLET_PASSWORD at minimum +# docker compose -f docker-compose.node.yml up -d +# +# What it does: +# - Syncs and validates the Lethean blockchain +# - Peers with other nodes (P2P port 46942) +# - Runs a wallet that stakes automatically (PoS mining) +# - Optionally mines with ProgPoWZ (connect external GPU miner) +# +# Earnings: +# - PoS staking rewards (proportional to balance) +# - PoW block rewards if mining +# +# Ports: +# 46941 — Daemon RPC (local tools) +# 46942 — P2P (open this on your router for full node) +# 46944 — Wallet RPC (local tools) + +services: + daemon: + image: lthn/chain:testnet + container_name: lthn-node + restart: unless-stopped + ports: + - "${DAEMON_RPC_PORT:-46941}:36941" + - "${DAEMON_P2P_PORT:-46942}:36942" + volumes: + - chain-data:/data + entrypoint: + - lethean-chain-node + command: + - --data-dir + - /data + - --rpc-bind-ip + - "0.0.0.0" + - --rpc-bind-port + - "36941" + - --p2p-bind-port + - "36942" + - --rpc-enable-admin-api + - --allow-local-ip + - --log-level + - "${DAEMON_LOG_LEVEL:-1}" + - --disable-upnp + healthcheck: + test: ["CMD-SHELL", "curl -sf http://localhost:36941/json_rpc -d '{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"getinfo\"}' -H 'Content-Type: application/json' | grep -q OK"] + interval: 30s + timeout: 10s + retries: 5 + start_period: 60s + + wallet: + image: lthn/chain:testnet + container_name: lthn-wallet + restart: unless-stopped + ports: + - "${WALLET_RPC_PORT:-46944}:36944" + volumes: + - wallet-data:/wallet + entrypoint: + - sh + - -c + command: + - | + if [ ! -f /wallet/node.wallet ]; then + echo '${WALLET_PASSWORD:-}' | lethean-wallet-cli --generate-new-wallet /wallet/node.wallet --password '${WALLET_PASSWORD:-}' --daemon-address daemon:36941 --command exit; + fi; + lethean-wallet-cli \ + --wallet-file /wallet/node.wallet \ + --password '${WALLET_PASSWORD:-}' \ + --daemon-address daemon:36941 \ + --rpc-bind-port 36944 \ + --rpc-bind-ip 0.0.0.0 \ + --do-pos-mining + depends_on: + daemon: + condition: service_healthy + +volumes: + chain-data: + wallet-data: diff --git a/docker/test-network.sh b/docker/test-network.sh new file mode 100755 index 00000000..e1112861 --- /dev/null +++ b/docker/test-network.sh @@ -0,0 +1,90 @@ +#!/bin/bash +# Lethean testnet network validation script +# Tests: node sync, mining, wallet, block propagation +# +# Usage: ./test-network.sh [compose-file] +# Default: docker-compose.nodes.yml + +set -e +COMPOSE=${1:-docker-compose.nodes.yml} + +echo "=== Lethean Network Test ===" +echo "Using: $COMPOSE" +echo "" + +# Check all nodes are running +echo "1. Checking node health..." +for n in 1 2 3; do + port=$((46940 + (n-1)*10 + 1)) + height=$(curl -s -X POST http://127.0.0.1:$port/json_rpc \ + -H 'Content-Type: application/json' \ + -d '{"jsonrpc":"2.0","id":"0","method":"getinfo"}' 2>/dev/null | \ + python3 -c "import sys,json; print(json.load(sys.stdin)['result']['height'])" 2>/dev/null) + echo " node$n (:$port): height=$height" +done + +echo "" +echo "2. Checking P2P connectivity..." +peers=$(curl -s -X POST http://127.0.0.1:46941/json_rpc \ + -H 'Content-Type: application/json' \ + -d '{"jsonrpc":"2.0","id":"0","method":"getinfo"}' 2>/dev/null | \ + python3 -c "import sys,json; d=json.load(sys.stdin)['result']; print(f'out={d[\"outgoing_connections_count\"]} in={d[\"incoming_connections_count\"]}')" 2>/dev/null) +echo " node1 peers: $peers" + +echo "" +echo "3. Testing getblocktemplate..." +template=$(curl -s -X POST http://127.0.0.1:46941/json_rpc \ + -H 'Content-Type: application/json' \ + -d '{"jsonrpc":"2.0","id":"0","method":"getblocktemplate","params":{"wallet_address":"iTHNUNiuu3VP1yy8xH2y5iQaABKXurdjqZmzFiBiyR4dKG3j6534e9jMriY6SM7PH8NibVwVWW1DWJfQEWnSjS8n3Wgx86pQpY"}}' 2>/dev/null | \ + python3 -c "import sys,json; r=json.load(sys.stdin)['result']; print(f'height={r[\"height\"]} diff={r[\"difficulty\"]}')" 2>/dev/null) +echo " template: $template" + +echo "" +echo "4. Starting mining on node1..." +curl -s -X POST http://127.0.0.1:46941/start_mining \ + -H 'Content-Type: application/json' \ + -d '{"miner_address":"iTHNUNiuu3VP1yy8xH2y5iQaABKXurdjqZmzFiBiyR4dKG3j6534e9jMriY6SM7PH8NibVwVWW1DWJfQEWnSjS8n3Wgx86pQpY","threads_count":4}' 2>/dev/null | python3 -m json.tool 2>/dev/null + +echo "" +echo "5. Waiting 60s for blocks..." +sleep 60 + +echo "" +echo "6. Checking sync across nodes..." +heights="" +for n in 1 2 3; do + port=$((46940 + (n-1)*10 + 1)) + h=$(curl -s -X POST http://127.0.0.1:$port/json_rpc \ + -H 'Content-Type: application/json' \ + -d '{"jsonrpc":"2.0","id":"0","method":"getinfo"}' 2>/dev/null | \ + python3 -c "import sys,json; print(json.load(sys.stdin)['result']['height'])" 2>/dev/null) + echo " node$n: height=$h" + heights="$heights $h" +done + +# Check all nodes at same height +unique=$(echo $heights | tr ' ' '\n' | sort -u | wc -l) +if [ "$unique" -eq 1 ]; then + echo "" + echo " PASS: All nodes synced at same height" +else + echo "" + echo " WARN: Nodes at different heights (propagation delay normal)" +fi + +echo "" +echo "7. Checking HF status..." +curl -s -X POST http://127.0.0.1:46941/json_rpc \ + -H 'Content-Type: application/json' \ + -d '{"jsonrpc":"2.0","id":"0","method":"getinfo"}' 2>/dev/null | \ + python3 -c " +import sys,json +d=json.load(sys.stdin)['result'] +hf=[i for i,v in enumerate(d['is_hardfok_active']) if v] +print(f' Active HFs: {hf}') +print(f' PoS allowed: {d[\"pos_allowed\"]}') +print(f' Aliases: {d[\"alias_count\"]}') +" + +echo "" +echo "=== Test Complete ==="