From 3fd1af98248783db3dd23b4a093b9d7cbce5836b Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 3 Apr 2026 13:20:40 +0100 Subject: [PATCH] feat(docker): single-container staking node for GUI integration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dockerfile.staking-node — runs daemon + wallet with PoS staking in one container using supervisord. No docker-compose needed. For the CoreGUI desktop app to run on Windows/Mac/Linux: docker run -d -p 46941:36941 -p 46942:36942 -p 46944:36944 \ -v lthn-chain:/data -v lthn-wallet:/wallet \ -e WALLET_PASSWORD=pass \ lthn/staking-node Built-in helpers: docker exec /status.sh — chain + wallet status docker exec /wallet-address.sh — get wallet address Co-Authored-By: Charon --- docker/Dockerfile.staking-node | 182 +++++++++++++++++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 docker/Dockerfile.staking-node diff --git a/docker/Dockerfile.staking-node b/docker/Dockerfile.staking-node new file mode 100644 index 00000000..0a05932d --- /dev/null +++ b/docker/Dockerfile.staking-node @@ -0,0 +1,182 @@ +# Lethean Staking Node — single container, no compose needed +# Runs daemon + wallet with PoS staking in one container. +# +# Build: +# docker build -f Dockerfile.staking-node -t lthn/staking-node . +# +# Run: +# docker run -d --name lthn-staker \ +# -p 46941:36941 -p 46942:36942 -p 46944:36944 \ +# -v lthn-chain:/data -v lthn-wallet:/wallet \ +# lthn/staking-node +# +# Run with password: +# docker run -d --name lthn-staker \ +# -e WALLET_PASSWORD=my-pass \ +# -p 46941:36941 -p 46942:36942 -p 46944:36944 \ +# -v lthn-chain:/data -v lthn-wallet:/wallet \ +# lthn/staking-node +# +# Check status: +# docker exec lthn-staker /status.sh +# +# Get wallet address: +# docker exec lthn-staker /wallet-address.sh +# +# Ports: +# 36941 — Daemon RPC (chain queries) +# 36942 — P2P (open on router for full node) +# 36944 — Wallet RPC (balance, transfers) + +FROM lthn/chain:testnet + +RUN apt-get update && apt-get install -y --no-install-recommends \ + curl supervisor bc \ + && rm -rf /var/lib/apt/lists/* + +# Supervisor manages both daemon and wallet in one container +COPY <<'SUPERVISOR' /etc/supervisor/conf.d/lethean.conf +[supervisord] +nodaemon=true +logfile=/dev/stdout +logfile_maxbytes=0 +loglevel=info + +[program:daemon] +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 %(ENV_DAEMON_LOG_LEVEL)s + --disable-upnp +autorestart=true +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 + +[program:wallet] +command=/start-wallet.sh +autorestart=true +startsecs=30 +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 +SUPERVISOR + +# Wallet startup script — waits for daemon, creates wallet if needed +COPY <<'WALLETSH' /start-wallet.sh +#!/bin/bash +set -e + +WALLET_FILE="/wallet/staker.wallet" +WALLET_PASS="${WALLET_PASSWORD:-}" +DAEMON="127.0.0.1:36941" + +# Wait for daemon RPC +echo "[wallet] Waiting for daemon..." +until curl -sf "http://$DAEMON/json_rpc" \ + -d '{"jsonrpc":"2.0","id":"0","method":"getinfo"}' \ + -H 'Content-Type: application/json' > /dev/null 2>&1; do + sleep 5 +done +echo "[wallet] Daemon ready" + +# Create wallet if it doesn't exist +if [ ! -f "$WALLET_FILE" ]; then + echo "[wallet] Creating new wallet..." + echo "$WALLET_PASS" | lethean-wallet-cli \ + --generate-new-wallet "$WALLET_FILE" \ + --password "$WALLET_PASS" \ + --daemon-address "$DAEMON" \ + --command exit + echo "[wallet] Wallet created" +fi + +# Start wallet with PoS staking +exec lethean-wallet-cli \ + --wallet-file "$WALLET_FILE" \ + --password "$WALLET_PASS" \ + --daemon-address "$DAEMON" \ + --rpc-bind-port 36944 \ + --rpc-bind-ip 0.0.0.0 \ + --do-pos-mining +WALLETSH + +# Status helper — shows chain + wallet in one command +COPY <<'STATUSSH' /status.sh +#!/bin/bash +echo "=== Lethean Staking Node ===" +echo "" + +# Chain +INFO=$(curl -sf http://127.0.0.1:36941/json_rpc \ + -d '{"jsonrpc":"2.0","id":"0","method":"getinfo"}' \ + -H 'Content-Type: application/json' 2>/dev/null) + +if [ -n "$INFO" ]; then + echo "$INFO" | python3 -c " +import sys,json +d=json.load(sys.stdin)['result'] +hf=d.get('is_hardfok_active',[]) +active=sum(1 for h in hf if h) +print(f'Chain:') +print(f' Height: {d[\"height\"]:,}') +print(f' Hardforks: HF0-{active-1}') +print(f' PoW diff: {d.get(\"pow_difficulty\",0):,}') +print(f' Aliases: {d.get(\"alias_count\",0)}') +print(f' Peers in: {d.get(\"incoming_connections_count\",0)}') +print(f' Peers out: {d.get(\"outgoing_connections_count\",0)}') +print(f' Status: {d[\"status\"]}') +" 2>/dev/null +else + echo "Chain: starting..." +fi + +echo "" + +# Wallet +BAL=$(curl -sf http://127.0.0.1:36944/json_rpc \ + -d '{"jsonrpc":"2.0","id":"0","method":"getbalance"}' \ + -H 'Content-Type: application/json' 2>/dev/null) + +if [ -n "$BAL" ]; then + echo "$BAL" | python3 -c " +import sys,json +d=json.load(sys.stdin)['result'] +print(f'Wallet:') +print(f' Balance: {d[\"balance\"]/1e12:.4f} LTHN') +print(f' Unlocked: {d[\"unlocked_balance\"]/1e12:.4f} LTHN') +print(f' Staking: yes (PoS mining active)') +" 2>/dev/null +else + echo "Wallet: syncing..." +fi +STATUSSH + +# Wallet address helper +COPY <<'ADDRSH' /wallet-address.sh +#!/bin/bash +curl -sf http://127.0.0.1:36944/json_rpc \ + -d '{"jsonrpc":"2.0","id":"0","method":"getaddress"}' \ + -H 'Content-Type: application/json' | python3 -c " +import sys,json +d=json.load(sys.stdin)['result'] +print(d['address']) +" 2>/dev/null || echo "Wallet not ready yet — daemon still syncing" +ADDRSH + +RUN chmod +x /start-wallet.sh /status.sh /wallet-address.sh + +ENV DAEMON_LOG_LEVEL=1 +ENV WALLET_PASSWORD= + +EXPOSE 36941 36942 36944 + +VOLUME ["/data", "/wallet"] + +ENTRYPOINT ["supervisord", "-c", "/etc/supervisor/conf.d/lethean.conf"]