feat(docker): full ecosystem Docker deployment
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

- docker-compose.pull.yml: production compose with pre-built images,
  health checks, env var config, service dependencies
- docker-compose.local.yml: override for mounting existing chain data
- docker-compose.ecosystem.yml: build-from-source compose with pool,
  LNS, Redis added
- Chain Dockerfile: add curl for health checks, swagger API resources
- Pool Dockerfile: Ubuntu 24.04, Boost 1.83, native ProgPoWZ compilation
- .env.example: configurable passwords, ports, hostname
- pool-config.json: Docker-networked pool config
- health.sh: one-command stack health check
- deploy.sh: remote server deployment script
- lethean-testnet.service: systemd auto-start unit
- README.md: quickstart, mining guide, backup, troubleshooting

All 8 services tested and running in Docker:
daemon, wallet, explorer, trade-api, trade-frontend, pool, LNS, docs

Co-Authored-By: Charon <charon@lethean.io>
This commit is contained in:
Claude 2026-04-03 11:14:30 +01:00
parent b61d773a0f
commit 91efcd41d0
No known key found for this signature in database
GPG key ID: AF404715446AEB41
11 changed files with 1054 additions and 2 deletions

1
.gitignore vendored
View file

@ -15,3 +15,4 @@ Thumbs.db
.vs/*
CMakeUserPresets.json
ConanPresets.json
docker/dist/

38
docker/.env.example Normal file
View file

@ -0,0 +1,38 @@
# 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

134
docker/README.md Normal file
View file

@ -0,0 +1,134 @@
# Lethean Testnet — Docker Deployment
Run the full Lethean ecosystem in Docker. No compilation needed.
## Quick Start
```bash
# 1. Configure
cp .env.example .env
# Edit .env — at minimum change JWT_SECRET and WALLET_PASSWORD
# 2. Start
docker compose -f docker-compose.pull.yml up -d
# 3. Check
docker compose -f docker-compose.pull.yml ps
```
The daemon takes ~30 seconds to initialise the chain database on first run.
Other services wait for the daemon health check before starting.
## Services
| Service | URL | Description |
|---------|-----|-------------|
| Block Explorer | http://localhost:3335 | Browse blocks, transactions, aliases |
| Trade DEX | http://localhost:3338 | Decentralised exchange frontend |
| Trade API | http://localhost:3336 | REST API for trade operations |
| Mining Pool | http://localhost:2117 | Pool stats API |
| Pool Stratum | localhost:5555 | Connect miners here |
| LNS | http://localhost:5553 | Lethean Name Service HTTP API |
| LNS DNS | localhost:5354 | DNS resolver for .lthn names |
| Daemon RPC | http://localhost:46941 | Chain node JSON-RPC |
| Wallet RPC | http://localhost:46944 | Wallet JSON-RPC |
| Docs | http://localhost:8099 | Documentation site |
## Mining
Connect a ProgPoWZ miner to the pool stratum:
```
stratum+tcp://YOUR_IP:5555
```
Compatible miners:
- **progminer** (recommended) — `progminer -P stratum+tcp://YOUR_WALLET_ADDRESS@localhost:5555`
- **T-Rex** — ProgPoWZ algorithm
- **TeamRedMiner** — ProgPoWZ algorithm
## Wallet
The wallet auto-creates on first start. To check your balance:
```bash
curl -s http://localhost:46944/json_rpc \
-d '{"jsonrpc":"2.0","id":"0","method":"getbalance"}' \
-H 'Content-Type: application/json'
```
The wallet runs with PoS mining enabled (`--do-pos-mining`), so it will stake
any balance automatically.
## Chain Status
```bash
curl -s http://localhost:46941/json_rpc \
-d '{"jsonrpc":"2.0","id":"0","method":"getinfo"}' \
-H 'Content-Type: application/json' | python3 -m json.tool
```
## Volumes
| Volume | Contains | Backup? |
|--------|----------|---------|
| `chain-data` | Blockchain database (LMDB) | Optional — can resync |
| `wallet-data` | Wallet file + keys | **Critical — back up regularly** |
| `explorer-db` | Explorer PostgreSQL | Can recreate from chain |
| `trade-db` | Trade PostgreSQL | Important for trade history |
### Backup wallet
```bash
docker compose -f docker-compose.pull.yml stop wallet
docker cp lthn-wallet:/wallet ./wallet-backup-$(date +%Y%m%d)
docker compose -f docker-compose.pull.yml start wallet
```
## Ports
All ports can be changed via `.env`. See `.env.example` for the full list.
If you're running behind a firewall and want external access:
- **P2P:** Open port 46942 (TCP) for other nodes to connect
- **Stratum:** Open port 5555 (TCP) for external miners
- **Explorer:** Open port 3335 (TCP) for public block explorer
## Troubleshooting
**Daemon crashes on start:**
Check logs with `docker logs lthn-daemon`. Common causes:
- Port already in use — change ports in `.env`
- Corrupt chain data — remove volume: `docker volume rm docker_chain-data`
**Wallet shows 0 balance:**
The wallet needs to sync with the daemon first. Check sync status in daemon logs.
New wallets start empty — mine or receive LTHN to fund them.
**Pool shows "Daemon died":**
The pool needs both the daemon and wallet running. Check both are healthy:
```bash
docker compose -f docker-compose.pull.yml ps daemon wallet
```
**Explorer shows "offline":**
The explorer connects to the daemon internally. If the daemon is still syncing,
the explorer will show offline until sync completes.
## Stopping
```bash
docker compose -f docker-compose.pull.yml down # stop containers
docker compose -f docker-compose.pull.yml down -v # stop + delete data
```
## Building from Source
If you want to build the images yourself instead of pulling pre-built ones:
```bash
docker compose -f docker-compose.ecosystem.yml build
docker compose -f docker-compose.ecosystem.yml up -d
```
This requires the full source tree — see the main repository README.

66
docker/deploy.sh Executable file
View file

@ -0,0 +1,66 @@
#!/bin/bash
# Lethean Testnet — Production Deploy Script
# Transfers images and config to a remote server and starts the stack.
#
# Usage:
# bash deploy.sh user@server
# bash deploy.sh user@server /opt/lethean/docker
#
# Prerequisites on the remote server:
# - Docker + Docker Compose v2
# - SSH access
set -e
REMOTE="${1:?Usage: deploy.sh user@server [install_dir]}"
INSTALL_DIR="${2:-/opt/lethean/docker}"
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
IMAGES="$SCRIPT_DIR/dist/lethean-testnet-images.tar.gz"
if [ ! -f "$IMAGES" ]; then
echo "Building image bundle..."
mkdir -p "$SCRIPT_DIR/dist"
docker save \
lthn/chain:testnet \
lthn/explorer:testnet \
lthn/trade-api:testnet \
lthn/trade-frontend:testnet \
lthn/pool:testnet \
lthn/lns:testnet \
lthn/docs:testnet \
| gzip > "$IMAGES"
fi
echo "Image bundle: $(du -h "$IMAGES" | cut -f1)"
echo "Deploying to $REMOTE:$INSTALL_DIR"
echo ""
# Create remote directory
ssh "$REMOTE" "mkdir -p $INSTALL_DIR"
# Transfer files
echo "Transferring config files..."
scp "$SCRIPT_DIR/docker-compose.pull.yml" "$REMOTE:$INSTALL_DIR/"
scp "$SCRIPT_DIR/.env.example" "$REMOTE:$INSTALL_DIR/"
scp "$SCRIPT_DIR/pool-config.json" "$REMOTE:$INSTALL_DIR/"
scp "$SCRIPT_DIR/health.sh" "$REMOTE:$INSTALL_DIR/"
scp "$SCRIPT_DIR/lethean-testnet.service" "$REMOTE:$INSTALL_DIR/"
# Create .env if it doesn't exist
ssh "$REMOTE" "[ -f $INSTALL_DIR/.env ] || cp $INSTALL_DIR/.env.example $INSTALL_DIR/.env"
echo "Transferring images (~$(du -h "$IMAGES" | cut -f1))... this may take a while"
scp "$IMAGES" "$REMOTE:/tmp/lethean-testnet-images.tar.gz"
echo "Loading images on remote..."
ssh "$REMOTE" "docker load < /tmp/lethean-testnet-images.tar.gz && rm /tmp/lethean-testnet-images.tar.gz"
echo "Starting stack..."
ssh "$REMOTE" "cd $INSTALL_DIR && docker compose -f docker-compose.pull.yml up -d"
echo ""
echo "Deploy complete. Run health check:"
echo " ssh $REMOTE 'cd $INSTALL_DIR && bash health.sh'"
echo ""
echo "Install systemd service (optional):"
echo " ssh $REMOTE 'sudo cp $INSTALL_DIR/lethean-testnet.service /etc/systemd/system/ && sudo systemctl daemon-reload && sudo systemctl enable lethean-testnet'"

View file

@ -0,0 +1,200 @@
# Lethean Full Ecosystem
# Chain node + wallet + explorer + trade + pool + LNS + docs
#
# Usage:
# docker compose -f docker-compose.ecosystem.yml up -d
#
# URLs after startup:
# Explorer: http://localhost:3335
# Trade: http://localhost:3337
# Trade API: http://localhost:3336
# Pool API: http://localhost:2117
# Pool Web: http://localhost:8888
# LNS HTTP: http://localhost:5553
# LNS DNS: localhost:5354
# Docs: http://localhost:8098
# Daemon RPC: http://localhost:46941
# Wallet RPC: http://localhost:46944
services:
# --- Chain ---
daemon:
build:
context: ..
dockerfile: utils/docker/lthn-chain/Dockerfile
target: chain-service
args:
BUILD_TESTNET: 1
BUILD_THREADS: 4
container_name: lthn-daemon
ports:
- "46941:36941"
- "46942:36942"
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
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"
volumes:
- wallet-data:/wallet
entrypoint: >
sh -c "
if [ ! -f /wallet/main.wallet ]; then
echo '' | lethean-wallet-cli --generate-new-wallet /wallet/main.wallet --password '' --daemon-address daemon:36941 --command exit;
fi;
lethean-wallet-cli
--wallet-file /wallet/main.wallet
--password ''
--daemon-address daemon:36941
--rpc-bind-port 36944
--rpc-bind-ip 0.0.0.0
--do-pos-mining
"
depends_on:
- daemon
# --- 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:/var/lib/postgresql/data
explorer:
build:
context: ../../zano-upstream/zano-explorer-zarcanum
container_name: lthn-explorer
ports:
- "3335:3335"
environment:
API: http://daemon:36941
FRONTEND_API: http://localhost:3335
SERVER_PORT: "3335"
AUDITABLE_WALLET_API: http://daemon:36941
PGUSER: explorer
PGPASSWORD: explorer
PGDATABASE: lethean_explorer
PGHOST: explorer-db
PGPORT: "5432"
MEXC_API_URL: ""
depends_on:
- daemon
- explorer-db
# --- Trade ---
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:/var/lib/postgresql/data
trade-api:
build:
context: ../../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://daemon:36941/json_rpc
depends_on:
- daemon
- trade-db
trade-frontend:
build:
context: ../../zano-upstream/zano_trade_frontend
container_name: lthn-trade-frontend
ports:
- "3337:30289"
environment:
NEXT_PUBLIC_API_URL: http://trade-api:3336
depends_on:
- trade-api
# --- Mining Pool ---
pool-redis:
image: redis:7-alpine
container_name: lthn-pool-redis
restart: unless-stopped
pool:
build:
context: ../..
dockerfile: zano-upstream/zano-pool/Dockerfile
container_name: lthn-pool
ports:
- "5555:5555"
- "7777:7777"
- "2117:2117"
- "8888:8888"
volumes:
- ./pool-config.json:/pool/config.json:ro
depends_on:
- daemon
- wallet
- pool-redis
# --- LNS (Lethean Name Service) ---
lns:
build:
context: ../../lns
container_name: lthn-lns
ports:
- "5553:5553" # HTTP API
- "5354:5354/udp" # DNS
- "5354:5354/tcp" # DNS (TCP)
environment:
DAEMON_RPC: http://daemon:36941/json_rpc
LNS_MODE: light
LNS_HTTP_PORT: "5553"
LNS_DNS_PORT: "5354"
depends_on:
- daemon
# --- Docs ---
docs:
build:
context: ../../zano-upstream/zano-docs
container_name: lthn-docs
ports:
- "8098:80"
volumes:
daemon-data:
wallet-data:
explorer-db:
trade-db:

View file

@ -0,0 +1,7 @@
# Local override — mounts existing testnet chain data
# Usage: docker compose -f docker-compose.pull.yml -f docker-compose.local.yml up -d
services:
daemon:
volumes:
- /opt/lethean/testnet-data:/data

View file

@ -0,0 +1,229 @@
# Lethean Testnet Ecosystem
# Full node + wallet + explorer + trade + pool + LNS
#
# Quick start:
# cp .env.example .env # edit passwords/hostname
# docker compose -f docker-compose.pull.yml up -d
# docker compose -f docker-compose.pull.yml logs -f daemon
#
# Services:
# Explorer: http://${PUBLIC_HOST:-localhost}:3335
# Trade: http://${PUBLIC_HOST:-localhost}:3338
# Trade API: http://${PUBLIC_HOST:-localhost}:3336
# Pool API: http://${PUBLIC_HOST:-localhost}:2117
# LNS HTTP: http://${PUBLIC_HOST:-localhost}:5553
# Docs: http://${PUBLIC_HOST:-localhost}:8099
# Daemon RPC: http://${PUBLIC_HOST:-localhost}:46941
# Wallet RPC: http://${PUBLIC_HOST:-localhost}:46944
services:
# --- Chain ---
daemon:
image: lthn/chain:testnet
container_name: lthn-daemon
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: 30s
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/main.wallet ]; then
echo '${WALLET_PASSWORD:-}' | lethean-wallet-cli --generate-new-wallet /wallet/main.wallet --password '${WALLET_PASSWORD:-}' --daemon-address daemon:36941 --command exit;
fi;
lethean-wallet-cli \
--wallet-file /wallet/main.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
# --- Explorer ---
explorer-db:
image: postgres:16-alpine
container_name: lthn-explorer-db
restart: unless-stopped
environment:
POSTGRES_USER: ${EXPLORER_DB_USER:-explorer}
POSTGRES_PASSWORD: ${EXPLORER_DB_PASS:-explorer}
POSTGRES_DB: ${EXPLORER_DB_NAME:-lethean_explorer}
volumes:
- explorer-db:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${EXPLORER_DB_USER:-explorer}"]
interval: 10s
timeout: 5s
retries: 5
explorer:
image: lthn/explorer:testnet
container_name: lthn-explorer
restart: unless-stopped
ports:
- "${EXPLORER_PORT:-3335}:3335"
environment:
API: http://daemon:36941
FRONTEND_API: http://${PUBLIC_HOST:-localhost}:${EXPLORER_PORT:-3335}
SERVER_PORT: "3335"
AUDITABLE_WALLET_API: http://daemon:36941
PGUSER: ${EXPLORER_DB_USER:-explorer}
PGPASSWORD: ${EXPLORER_DB_PASS:-explorer}
PGDATABASE: ${EXPLORER_DB_NAME:-lethean_explorer}
PGHOST: explorer-db
PGPORT: "5432"
MEXC_API_URL: ""
depends_on:
daemon:
condition: service_healthy
explorer-db:
condition: service_healthy
# --- Trade ---
trade-db:
image: postgres:16-alpine
container_name: lthn-trade-db
restart: unless-stopped
environment:
POSTGRES_USER: ${TRADE_DB_USER:-trade}
POSTGRES_PASSWORD: ${TRADE_DB_PASS:-trade}
POSTGRES_DB: ${TRADE_DB_NAME:-lethean_trade}
volumes:
- trade-db:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${TRADE_DB_USER:-trade}"]
interval: 10s
timeout: 5s
retries: 5
trade-api:
image: lthn/trade-api:testnet
container_name: lthn-trade-api
restart: unless-stopped
ports:
- "${TRADE_API_PORT:-3336}:3336"
environment:
PORT: "3336"
PGUSER: ${TRADE_DB_USER:-trade}
PGPASSWORD: ${TRADE_DB_PASS:-trade}
PGDATABASE: ${TRADE_DB_NAME:-lethean_trade}
PGHOST: trade-db
PGPORT: "5432"
JWT_SECRET: ${JWT_SECRET:-change-me-before-production}
DAEMON_RPC_URL: http://daemon:36941/json_rpc
depends_on:
daemon:
condition: service_healthy
trade-db:
condition: service_healthy
trade-frontend:
image: lthn/trade-frontend:testnet
container_name: lthn-trade-frontend
restart: unless-stopped
ports:
- "${TRADE_FRONTEND_PORT:-3338}:30289"
environment:
NEXT_PUBLIC_API_URL: http://trade-api:3336
depends_on:
- trade-api
# --- Mining Pool ---
pool-redis:
image: redis:7-alpine
container_name: lthn-pool-redis
restart: unless-stopped
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
pool:
image: lthn/pool:testnet
container_name: lthn-pool
restart: unless-stopped
ports:
- "${POOL_STRATUM_PORT:-5555}:5555"
- "${POOL_API_PORT:-2117}:2117"
- "7777:7777"
- "8888:8888"
volumes:
- ./pool-config.json:/pool/config.json:ro
depends_on:
daemon:
condition: service_healthy
wallet:
condition: service_started
pool-redis:
condition: service_healthy
# --- LNS (Lethean Name Service) ---
lns:
image: lthn/lns:testnet
container_name: lthn-lns
restart: unless-stopped
ports:
- "${LNS_HTTP_PORT:-5553}:5553"
- "${LNS_DNS_PORT:-5354}:5354/udp"
- "${LNS_DNS_PORT:-5354}:5354/tcp"
environment:
DAEMON_RPC: http://daemon:36941/json_rpc
LNS_MODE: light
LNS_HTTP_PORT: "5553"
LNS_DNS_PORT: "5354"
depends_on:
daemon:
condition: service_healthy
# --- Docs ---
docs:
image: lthn/docs:testnet
container_name: lthn-docs
restart: unless-stopped
ports:
- "${DOCS_PORT:-8099}:80"
volumes:
chain-data:
wallet-data:
explorer-db:
trade-db:

58
docker/health.sh Executable file
View file

@ -0,0 +1,58 @@
#!/bin/bash
# Lethean Testnet Health Check
# Run: bash health.sh
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
check() {
local name="$1"
local url="$2"
local code
code=$(curl -sf -o /dev/null -w "%{http_code}" "$url" 2>/dev/null)
if [ "$code" = "200" ] || [ "$code" = "307" ] || [ "$code" = "404" ]; then
printf " ${GREEN}%-16s${NC} %s ${YELLOW}(HTTP %s)${NC}\n" "$name" "$url" "$code"
else
printf " ${RED}%-16s${NC} %s ${RED}(DOWN)${NC}\n" "$name" "$url"
fi
}
rpc() {
local name="$1"
local url="$2"
local method="$3"
local result
result=$(curl -sf "$url" -d "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"$method\"}" -H 'Content-Type: application/json' 2>/dev/null)
if [ -n "$result" ]; then
printf " ${GREEN}%-16s${NC} %s\n" "$name" "$(echo "$result" | python3 -c "
import sys,json
d=json.load(sys.stdin).get('result',{})
if 'height' in d:
hf = d.get('is_hardfok_active',[])
active = sum(1 for h in hf if h)
print(f'height={d[\"height\"]}, HF0-{active-1} active, status={d.get(\"status\",\"?\")}')
elif 'balance' in d:
print(f'{d[\"balance\"]/1e12:.4f} LTHN ({d[\"unlocked_balance\"]/1e12:.4f} unlocked)')
else:
print(json.dumps(d)[:80])
" 2>/dev/null)"
else
printf " ${RED}%-16s${NC} %s ${RED}(DOWN)${NC}\n" "$name" "$url"
fi
}
echo ""
echo " Lethean Testnet Health Check"
echo " $(date)"
echo " ---"
rpc "Daemon" "http://localhost:${DAEMON_RPC_PORT:-46941}/json_rpc" "getinfo"
rpc "Wallet" "http://localhost:${WALLET_RPC_PORT:-46944}/json_rpc" "getbalance"
check "Explorer" "http://localhost:${EXPLORER_PORT:-3335}/"
check "Trade API" "http://localhost:${TRADE_API_PORT:-3336}/"
check "Trade Web" "http://localhost:${TRADE_FRONTEND_PORT:-3338}/"
check "Pool API" "http://localhost:${POOL_API_PORT:-2117}/stats"
check "LNS" "http://localhost:${LNS_HTTP_PORT:-5553}/"
check "Docs" "http://localhost:${DOCS_PORT:-8099}/"
echo ""

View file

@ -0,0 +1,15 @@
[Unit]
Description=Lethean Testnet Ecosystem
Requires=docker.service
After=docker.service
[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/opt/lethean/docker
ExecStart=/usr/bin/docker compose -f docker-compose.pull.yml up -d
ExecStop=/usr/bin/docker compose -f docker-compose.pull.yml down
TimeoutStartSec=120
[Install]
WantedBy=multi-user.target

301
docker/pool-config.json Normal file
View file

@ -0,0 +1,301 @@
{
"poolHost": "lethean.somewhere.com",
"coin": "Lethean",
"symbol": "LTHN",
"coinUnits": 1000000000000,
"coinDecimalPlaces": 12,
"coinDifficultyTarget": 120,
"blockchainExplorer": "https://explorer.lthn.io/block/{id}",
"transactionExplorer": "https://explorer.lthn.io/transaction/{id}",
"daemonType": "default",
"cnAlgorithm": "progpowz",
"cnVariant": 2,
"cnBlobType": 0,
"isRandomX": false,
"includeHeight": false,
"previousOffset": 7,
"offset": 2,
"isCryptonight": false,
"reward": 1000000000000,
"logging": {
"files": {
"level": "info",
"directory": "logs",
"flushInterval": 5,
"prefix": "Lethean"
},
"console": {
"level": "info",
"colors": true
}
},
"childPools": [],
"poolServer": {
"enabled": true,
"mergedMining": false,
"clusterForks": 3,
"poolAddress": "iTHNUNiuu3VP1yy8xH2y5iQaABKXurdjqZmzFiBiyR4dKG3j6534e9jMriY6SM7PH8NibVwVWW1DWJfQEWnSjS8n3Wgx86pQpY",
"intAddressPrefix": null,
"blockRefreshInterval": 1000,
"minerTimeout": 900,
"sslCert": "cert.pem",
"sslKey": "privkey.pem",
"sslCA": "chain.pem",
"ports": [
{
"port": 5555,
"difficulty": 50000,
"desc": "Low end hardware"
},
{
"port": 7777,
"difficulty": 500000,
"desc": "Mid/high end hardware"
},
{
"port": 8888,
"difficulty": 5000000,
"desc": "Nicehash, MRR"
}
],
"varDiff": {
"minDiff": 10000,
"maxDiff": 5000000,
"targetTime": 45,
"retargetTime": 60,
"variancePercent": 30,
"maxJump": 100
},
"paymentId": {
"addressSeparator": "+"
},
"fixedDiff": {
"enabled": true,
"addressSeparator": "."
},
"shareTrust": {
"enabled": true,
"min": 10,
"stepDown": 3,
"threshold": 10,
"penalty": 30
},
"banning": {
"enabled": true,
"time": 600,
"invalidPercent": 25,
"checkThreshold": 30
},
"slushMining": {
"enabled": false,
"weight": 300,
"blockTime": 60,
"lastBlockCheckRate": 1
}
},
"payments": {
"enabled": true,
"interval": 900,
"maxAddresses": 5,
"mixin": 10,
"priority": 0,
"transferFee": 10000000000,
"dynamicTransferFee": true,
"minerPayFee": true,
"minPayment": 1000000000000,
"maxPayment": 100000000000000,
"maxTransactionAmount": 100000000000000,
"denomination": 1000000000000
},
"blockUnlocker": {
"enabled": true,
"interval": 60,
"depth": 10,
"poolFee": 0.2,
"soloFee": 0.2,
"devDonation": 0.5,
"networkFee": 0.0
},
"api": {
"enabled": true,
"hashrateWindow": 600,
"updateInterval": 15,
"bindIp": "0.0.0.0",
"port": 2117,
"blocks": 30,
"payments": 30,
"password": "password",
"ssl": false,
"sslPort": 2119,
"sslCert": "cert.pem",
"sslKey": "privkey.pem",
"sslCA": "chain.pem",
"trustProxyIP": true
},
"zmq": {
"enabled": false,
"host": "127.0.0.1",
"port": 39995
},
"daemon": {
"host": "daemon",
"port": 36941
},
"wallet": {
"host": "wallet",
"port": 36944
},
"redis": {
"host": "pool-redis",
"port": 6379,
"db": 11,
"cleanupInterval": 15
},
"notifications": {
"emailTemplate": "email_templates/default.txt",
"emailSubject": {
"emailAdded": "Your email was registered",
"workerConnected": "Worker %WORKER_NAME% connected",
"workerTimeout": "Worker %WORKER_NAME% stopped hashing",
"workerBanned": "Worker %WORKER_NAME% banned",
"blockFound": "Block %HEIGHT% found !",
"blockUnlocked": "Block %HEIGHT% unlocked !",
"blockOrphaned": "Block %HEIGHT% orphaned !",
"payment": "We sent you a payment !"
},
"emailMessage": {
"emailAdded": "Your email has been registered to receive pool notifications.",
"workerConnected": "Your worker %WORKER_NAME% for address %MINER% is now connected from ip %IP%.",
"workerTimeout": "Your worker %WORKER_NAME% for address %MINER% has stopped submitting hashes on %LAST_HASH%.",
"workerBanned": "Your worker %WORKER_NAME% for address %MINER% has been banned.",
"blockFound": "Block found at height %HEIGHT% by miner %MINER% on %TIME%. Waiting maturity.",
"blockUnlocked": "Block mined at height %HEIGHT% with %REWARD% and %EFFORT% effort on %TIME%.",
"blockOrphaned": "Block orphaned at height %HEIGHT% :(",
"payment": "A payment of %AMOUNT% has been sent to %ADDRESS% wallet."
},
"telegramMessage": {
"workerConnected": "Your worker _%WORKER_NAME%_ for address _%MINER%_ is now connected from ip _%IP%_.",
"workerTimeout": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has stopped submitting hashes on _%LAST_HASH%_.",
"workerBanned": "Your worker _%WORKER_NAME%_ for address _%MINER%_ has been banned.",
"blockFound": "*Block found at height* _%HEIGHT%_ *by miner* _%MINER%_*! Waiting maturity.*",
"blockUnlocked": "*Block mined at height* _%HEIGHT%_ *with* _%REWARD%_ *and* _%EFFORT%_ *effort on* _%TIME%_*.*",
"blockOrphaned": "*Block orphaned at height* _%HEIGHT%_ *:(*",
"payment": "A payment of _%AMOUNT%_ has been sent."
}
},
"email": {
"enabled": false,
"fromAddress": "your@email.com",
"transport": "sendmail",
"sendmail": {
"path": "/usr/sbin/sendmail"
},
"smtp": {
"host": "smtp.example.com",
"port": 587,
"secure": false,
"auth": {
"user": "username",
"pass": "password"
},
"tls": {
"rejectUnauthorized": false
}
},
"mailgun": {
"key": "your-private-key",
"domain": "mg.yourdomain"
}
},
"telegram": {
"enabled": false,
"botName": "",
"token": "",
"channel": "",
"channelStats": {
"enabled": false,
"interval": 30
},
"botCommands": {
"stats": "/stats",
"report": "/report",
"notify": "/notify",
"blocks": "/blocks"
}
},
"monitoring": {
"daemon": {
"checkInterval": 60,
"rpcMethod": "getblockcount"
},
"wallet": {
"checkInterval": 60,
"rpcMethod": "getbalance"
}
},
"prices": {
"source": "tradeogre",
"currency": "USD"
},
"charts": {
"pool": {
"hashrate": {
"enabled": true,
"updateInterval": 60,
"stepInterval": 1800,
"maximumPeriod": 86400
},
"miners": {
"enabled": true,
"updateInterval": 60,
"stepInterval": 1800,
"maximumPeriod": 86400
},
"workers": {
"enabled": true,
"updateInterval": 60,
"stepInterval": 1800,
"maximumPeriod": 86400
},
"difficulty": {
"enabled": true,
"updateInterval": 1800,
"stepInterval": 10800,
"maximumPeriod": 604800
},
"price": {
"enabled": true,
"updateInterval": 1800,
"stepInterval": 10800,
"maximumPeriod": 604800
},
"profit": {
"enabled": true,
"updateInterval": 1800,
"stepInterval": 10800,
"maximumPeriod": 604800
}
},
"user": {
"hashrate": {
"enabled": true,
"updateInterval": 180,
"stepInterval": 1800,
"maximumPeriod": 86400
},
"worker_hashrate": {
"enabled": true,
"updateInterval": 60,
"stepInterval": 60,
"maximumPeriod": 86400
},
"payments": {
"enabled": true
}
},
"blocks": {
"enabled": true,
"days": 30
}
}
}

View file

@ -58,12 +58,15 @@ RUN if [ "$BUILD_TESTNET" = "1" ]; then \
# use --target=build-artifacts to return the binaries
FROM scratch AS build-artifacts
COPY --from=build /code/build/release/src/lethean-* /
COPY --from=build /code/build/release/src/lethean-* /bin/
COPY --from=build /code/build/release/share/ /share/
# use --target=chain-service to return a working chain node
FROM ubuntu:24.04 AS chain-service
COPY --from=build-artifacts --chmod=+x / /bin
RUN apt-get update && apt-get install -y --no-install-recommends curl && rm -rf /var/lib/apt/lists/*
COPY --from=build-artifacts --chmod=+x /bin/ /bin/
COPY --from=build-artifacts /share/ /share/
EXPOSE 36941
EXPOSE 36942