fix(pos): add --rpc-ignore-offline to all daemon configs
Some checks are pending
Build & Release / Linux x86_64 (push) Waiting to run
Build & Release / macOS ARM64 (push) Waiting to run
Build & Release / Create Release (push) Blocked by required conditions

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 <charon@lethean.io>
This commit is contained in:
Claude 2026-04-03 13:53:07 +01:00
parent 1e565495bb
commit a6773abaca
No known key found for this signature in database
GPG key ID: AF404715446AEB41
14 changed files with 914 additions and 0 deletions

54
docker/.env Normal file
View file

@ -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

View file

@ -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

View file

@ -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

77
docker/build-all-platforms.sh Executable file
View file

@ -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)"

View file

@ -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

View file

@ -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

View file

@ -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:

View file

@ -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

View file

@ -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:

View file

@ -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:

52
docker/lethean/.env Normal file
View file

@ -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

View file

@ -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

View file

@ -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:

90
docker/test-network.sh Executable file
View file

@ -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 ==="