feat(docker): add dev.sh with auto-funding and ATM app setup
- Add dev.sh script for managing regtest development environment - Implement cmd_fund to fund ATM app owner via Lightning.Pub API - Add --fund flag to cmd_up for automatic funding on startup - Update setup_atm_app to write VITE_APP_ID to machine .env - Fix Electron IPC to pass appId and extensionApiUrl to renderer - Restructure repo from nested lamassu-next/ to root The dev.sh script now supports: - ./dev.sh up --fund # Start regtest and auto-fund ATM - ./dev.sh fund # Fund existing ATM app - ./dev.sh status # Show environment status - ./dev.sh reset # Clean restart Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
30a2eb2199
commit
c98f126ba7
180 changed files with 2695 additions and 9587 deletions
1
docker/.state/atm-app-id
Normal file
1
docker/.state/atm-app-id
Normal file
|
|
@ -0,0 +1 @@
|
|||
02f5340554b29537f4b9c3bb6c131f69c08044b2114939849d3bec8c4df456e7
|
||||
1
docker/.state/atm-app-token
Normal file
1
docker/.state/atm-app-token
Normal file
|
|
@ -0,0 +1 @@
|
|||
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhcHBJZCI6IjAyZjUzNDA1NTRiMjk1MzdmNGI5YzNiYjZjMTMxZjY5YzA4MDQ0YjIxMTQ5Mzk4NDlkM2JlYzhjNGRmNDU2ZTciLCJpYXQiOjE3NzExODIwMTZ9.uE473GYJdB946daNCZ-5PqPe1JGihSM6KnL66n1AN7k
|
||||
1
docker/.state/lightning-pub-nprofile
Normal file
1
docker/.state/lightning-pub-nprofile
Normal file
|
|
@ -0,0 +1 @@
|
|||
nprofile1qyg8wue69uhhxarjvee8jw3hxumnwqpq35fnkv4nguyhrutu4tspkjlfaqs95pnnegzqkg24h040vn3852jshankcx
|
||||
1
docker/.state/lightning-pub-pubkey
Normal file
1
docker/.state/lightning-pub-pubkey
Normal file
|
|
@ -0,0 +1 @@
|
|||
8d133b32b3470971f17caae01b4be9e8205a0673ca040b2155bbeaf64e27a2a5
|
||||
655
docker/dev.sh
Executable file
655
docker/dev.sh
Executable file
|
|
@ -0,0 +1,655 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Lamassu Next Development Environment
|
||||
#
|
||||
# Single command to manage the complete development stack:
|
||||
# - Regtest Bitcoin/Lightning network (from ~/dev/local/docker/regtest)
|
||||
# - Lightning.Pub with configurable image/worktree
|
||||
# - Auto-configured ATM application
|
||||
#
|
||||
# Usage:
|
||||
# ./dev.sh up # Start everything
|
||||
# ./dev.sh up --fund # Start and auto-fund ATM with 100k sats
|
||||
# ./dev.sh up --fund=50000 # Start and auto-fund with specific amount
|
||||
# ./dev.sh up --worktree ~/path/to/lp # Build Lightning.Pub from worktree
|
||||
# ./dev.sh up --image myimage:tag # Use specific Docker image
|
||||
# ./dev.sh down # Stop everything
|
||||
# ./dev.sh atm # Launch ATM application
|
||||
# ./dev.sh status # Show connection info
|
||||
# ./dev.sh logs [service] # Follow logs
|
||||
# ./dev.sh fund [amount] # Fund ATM (default: 100000 sats)
|
||||
# ./dev.sh mine [blocks] # Mine blocks manually
|
||||
# ./dev.sh reset # Reset all state (fresh start)
|
||||
#
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
REGTEST_DIR="${REGTEST_DIR:-$HOME/dev/local/docker/regtest}"
|
||||
DEFAULT_IMAGE="lightning-pub-withdraw:latest"
|
||||
DEFAULT_FUNDING_SATS=100000
|
||||
|
||||
# State files
|
||||
STATE_DIR="$SCRIPT_DIR/.state"
|
||||
PUBKEY_FILE="$STATE_DIR/lightning-pub-pubkey"
|
||||
NPROFILE_FILE="$STATE_DIR/lightning-pub-nprofile"
|
||||
APP_TOKEN_FILE="$STATE_DIR/atm-app-token"
|
||||
APP_ID_FILE="$STATE_DIR/atm-app-id"
|
||||
|
||||
# Colors
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
CYAN='\033[0;36m'
|
||||
BOLD='\033[1m'
|
||||
DIM='\033[2m'
|
||||
NC='\033[0m'
|
||||
|
||||
log() { echo -e "${GREEN}►${NC} $1"; }
|
||||
warn() { echo -e "${YELLOW}⚠${NC} $1"; }
|
||||
error() { echo -e "${RED}✗${NC} $1"; }
|
||||
success() { echo -e "${GREEN}✓${NC} $1"; }
|
||||
|
||||
# Ensure state directory exists
|
||||
mkdir -p "$STATE_DIR"
|
||||
|
||||
#############################################################################
|
||||
# Helper Functions
|
||||
#############################################################################
|
||||
|
||||
get_local_ip() {
|
||||
ip route get 1 2>/dev/null | awk '{print $7; exit}' || hostname -I | awk '{print $1}'
|
||||
}
|
||||
|
||||
is_regtest_running() {
|
||||
# Check if lnd-4 container is actually running (not just network exists)
|
||||
docker ps --filter "name=lnbits-lnd-4-1" --format "{{.Names}}" 2>/dev/null | grep -q lnbits-lnd-4-1
|
||||
}
|
||||
|
||||
is_lnd4_ready() {
|
||||
# Use lnd-4 hostname (not localhost) since lnd binds to container IP
|
||||
docker exec lnbits-lnd-4-1 lncli --network=regtest --rpcserver=lnd-4:10009 getinfo &>/dev/null 2>&1
|
||||
}
|
||||
|
||||
is_lightning_pub_ready() {
|
||||
docker logs lamassu-lightning-pub 2>&1 | grep -q "LightningPub listening"
|
||||
}
|
||||
|
||||
get_lnd4_balance() {
|
||||
docker exec lnbits-lnd-4-1 lncli --network=regtest walletbalance 2>/dev/null | grep -oP '"total_balance":\s*"\K[0-9]+' || echo "0"
|
||||
}
|
||||
|
||||
#############################################################################
|
||||
# Regtest Management
|
||||
#############################################################################
|
||||
|
||||
start_regtest() {
|
||||
if is_regtest_running; then
|
||||
log "Regtest network already running"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ ! -d "$REGTEST_DIR" ]]; then
|
||||
error "Regtest directory not found: $REGTEST_DIR"
|
||||
echo " Clone it from: https://github.com/your-org/regtest-env"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "Starting regtest environment..."
|
||||
# Use project name "lnbits" to match the expected network name (lnbits_default)
|
||||
(cd "$REGTEST_DIR" && docker compose -p lnbits up -d)
|
||||
|
||||
# Wait for lnd-4 to be ready
|
||||
log "Waiting for lnd-4 to be ready..."
|
||||
local attempts=0
|
||||
while ! is_lnd4_ready && [[ $attempts -lt 30 ]]; do
|
||||
sleep 2
|
||||
attempts=$((attempts + 1))
|
||||
done
|
||||
|
||||
if is_lnd4_ready; then
|
||||
success "lnd-4 is ready"
|
||||
else
|
||||
error "lnd-4 failed to start"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
stop_regtest() {
|
||||
if [[ -d "$REGTEST_DIR" ]]; then
|
||||
log "Stopping regtest environment..."
|
||||
(cd "$REGTEST_DIR" && docker compose down)
|
||||
fi
|
||||
}
|
||||
|
||||
#############################################################################
|
||||
# Lightning.Pub Management
|
||||
#############################################################################
|
||||
|
||||
build_from_worktree() {
|
||||
local worktree="$1"
|
||||
local image_name="lightning-pub-dev:latest"
|
||||
|
||||
if [[ ! -d "$worktree" ]]; then
|
||||
error "Worktree not found: $worktree"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "Building Lightning.Pub from $worktree..."
|
||||
docker build -t "$image_name" "$worktree"
|
||||
echo "$image_name"
|
||||
}
|
||||
|
||||
wait_for_lightning_pub() {
|
||||
log "Waiting for Lightning.Pub..."
|
||||
local attempts=0
|
||||
while ! is_lightning_pub_ready && [[ $attempts -lt 45 ]]; do
|
||||
# Check for errors
|
||||
if docker logs lamassu-lightning-pub 2>&1 | grep -q "Error:"; then
|
||||
local err=$(docker logs lamassu-lightning-pub 2>&1 | grep "Error:" | tail -1)
|
||||
error "Lightning.Pub error: $err"
|
||||
return 1
|
||||
fi
|
||||
sleep 2
|
||||
attempts=$((attempts + 1))
|
||||
done
|
||||
|
||||
if is_lightning_pub_ready; then
|
||||
sleep 2 # Extra time for Nostr middleware
|
||||
success "Lightning.Pub is ready"
|
||||
return 0
|
||||
else
|
||||
error "Lightning.Pub failed to start (timeout)"
|
||||
docker logs lamassu-lightning-pub 2>&1 | tail -10
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
extract_lightning_pub_info() {
|
||||
local pubkey=$(docker logs lamassu-lightning-pub 2>&1 | grep -oP 'pubkey:\s*\K[a-f0-9]+' | tail -1)
|
||||
local nprofile=$(docker logs lamassu-lightning-pub 2>&1 | grep -oP 'nprofile:\s*\K\S+' | tail -1)
|
||||
|
||||
echo "$pubkey" > "$PUBKEY_FILE"
|
||||
echo "$nprofile" > "$NPROFILE_FILE"
|
||||
|
||||
echo "$pubkey"
|
||||
}
|
||||
|
||||
#############################################################################
|
||||
# ATM Configuration
|
||||
#############################################################################
|
||||
|
||||
update_atm_env() {
|
||||
local pubkey="$1"
|
||||
local env_file="$PROJECT_DIR/apps/machine/.env"
|
||||
|
||||
if [[ ! -f "$env_file" ]]; then
|
||||
warn "ATM .env not found, creating..."
|
||||
cat > "$env_file" << EOF
|
||||
# Lightning.Pub connection
|
||||
VITE_RELAY_URL=ws://localhost:7777
|
||||
VITE_LIGHTNING_PUB_PUBKEY=$pubkey
|
||||
VITE_LIGHTNING_PUB_API_URL=http://localhost:1776
|
||||
VITE_ADMIN_TOKEN=lamassu-dev-admin-token
|
||||
VITE_ATM_PRIVATE_KEY=f391a2c3fc734f443b0f685688a0441b5fb9805853c0023f570c5a3c6412b136
|
||||
|
||||
# Extension API (for LNURL-withdraw)
|
||||
VITE_EXTENSION_API_URL=http://localhost:1777
|
||||
EOF
|
||||
else
|
||||
# Update existing pubkey
|
||||
if grep -q "VITE_LIGHTNING_PUB_PUBKEY" "$env_file"; then
|
||||
sed -i "s/VITE_LIGHTNING_PUB_PUBKEY=.*/VITE_LIGHTNING_PUB_PUBKEY=$pubkey/" "$env_file"
|
||||
else
|
||||
echo "VITE_LIGHTNING_PUB_PUBKEY=$pubkey" >> "$env_file"
|
||||
fi
|
||||
fi
|
||||
success "Updated ATM .env with pubkey"
|
||||
}
|
||||
|
||||
setup_atm_app() {
|
||||
log "Creating ATM app..."
|
||||
|
||||
# Generate unique app name to avoid conflicts
|
||||
local app_name="atm-$(date +%s)"
|
||||
|
||||
local response=$(curl -s -X POST http://localhost:1776/api/admin/app/add \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer lamassu-dev-admin-token" \
|
||||
-d "{\"name\":\"$app_name\",\"allow_user_creation\":true}" 2>/dev/null)
|
||||
|
||||
if echo "$response" | grep -q '"status":"OK"'; then
|
||||
local app_id=$(echo "$response" | grep -oP '"id":"\K[^"]+')
|
||||
local app_token=$(echo "$response" | grep -oP '"auth_token":"\K[^"]+')
|
||||
|
||||
echo "$app_id" > "$APP_ID_FILE"
|
||||
echo "$app_token" > "$APP_TOKEN_FILE"
|
||||
|
||||
# Update ATM .env with app ID
|
||||
local env_file="$PROJECT_DIR/apps/machine/.env"
|
||||
if [[ -f "$env_file" ]]; then
|
||||
if grep -q "VITE_APP_ID" "$env_file"; then
|
||||
sed -i "s/VITE_APP_ID=.*/VITE_APP_ID=$app_id/" "$env_file"
|
||||
else
|
||||
echo "" >> "$env_file"
|
||||
echo "# ATM App ID for LNURL-withdraw" >> "$env_file"
|
||||
echo "VITE_APP_ID=$app_id" >> "$env_file"
|
||||
fi
|
||||
fi
|
||||
|
||||
success "Created ATM app: ${app_id:0:16}..."
|
||||
return 0
|
||||
else
|
||||
warn "Failed to create ATM app (may already exist)"
|
||||
# Try to use existing app from state file
|
||||
if [[ -f "$APP_ID_FILE" ]]; then
|
||||
log "Using existing app ID from state"
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
#############################################################################
|
||||
# Zeus Connection
|
||||
#############################################################################
|
||||
|
||||
generate_lndconnect() {
|
||||
local lnd_data="$REGTEST_DIR/data/lnd-3"
|
||||
local local_ip=$(get_local_ip)
|
||||
local rest_port=8082
|
||||
|
||||
local cert_path="$lnd_data/tls.cert"
|
||||
local mac_path="$lnd_data/data/chain/bitcoin/regtest/admin.macaroon"
|
||||
|
||||
if [[ ! -f "$cert_path" ]] || [[ ! -f "$mac_path" ]]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
local cert_b64=$(base64 -w0 "$cert_path" | tr '+/' '-_' | tr -d '=')
|
||||
local mac_b64=$(base64 -w0 "$mac_path" | tr '+/' '-_' | tr -d '=')
|
||||
|
||||
echo "lndconnect://${local_ip}:${rest_port}?cert=${cert_b64}&macaroon=${mac_b64}"
|
||||
}
|
||||
|
||||
#############################################################################
|
||||
# Display Functions
|
||||
#############################################################################
|
||||
|
||||
get_atm_balance() {
|
||||
# Get ATM app owner balance from Lightning.Pub logs
|
||||
local app_id=$(cat "$APP_ID_FILE" 2>/dev/null)
|
||||
if [[ -z "$app_id" ]]; then
|
||||
echo "not configured"
|
||||
return
|
||||
fi
|
||||
|
||||
# Query the app balance via admin API (if available)
|
||||
# For now, just indicate it's configured
|
||||
local app_token=$(cat "$APP_TOKEN_FILE" 2>/dev/null)
|
||||
if [[ -n "$app_token" ]]; then
|
||||
echo "configured (use './dev.sh fund' to add sats)"
|
||||
else
|
||||
echo "not configured"
|
||||
fi
|
||||
}
|
||||
|
||||
show_status() {
|
||||
local pubkey=$(cat "$PUBKEY_FILE" 2>/dev/null || docker logs lamassu-lightning-pub 2>&1 | grep -oP 'pubkey:\s*\K[a-f0-9]+' | tail -1)
|
||||
local nprofile=$(cat "$NPROFILE_FILE" 2>/dev/null || docker logs lamassu-lightning-pub 2>&1 | grep -oP 'nprofile:\s*\K\S+' | tail -1)
|
||||
local local_ip=$(get_local_ip)
|
||||
local lndconnect=$(generate_lndconnect 2>/dev/null || echo "")
|
||||
local lnd4_balance=$(get_lnd4_balance)
|
||||
local app_id=$(cat "$APP_ID_FILE" 2>/dev/null || echo "not set")
|
||||
|
||||
echo ""
|
||||
echo -e "${BOLD}╔═══════════════════════════════════════════════════════════════════╗${NC}"
|
||||
echo -e "${BOLD}║ LAMASSU NEXT - DEVELOPMENT ENVIRONMENT ║${NC}"
|
||||
echo -e "${BOLD}╚═══════════════════════════════════════════════════════════════════╝${NC}"
|
||||
echo ""
|
||||
|
||||
echo -e "${CYAN}Lightning.Pub${NC}"
|
||||
echo -e " Pubkey: ${GREEN}$pubkey${NC}"
|
||||
echo -e " nprofile: ${GREEN}$nprofile${NC}"
|
||||
echo ""
|
||||
|
||||
echo -e "${CYAN}Service URLs (local)${NC}"
|
||||
echo " Nostr Relay: ws://localhost:7777"
|
||||
echo " Lightning.Pub: http://localhost:1776"
|
||||
echo " Withdraw API: http://localhost:1777"
|
||||
echo ""
|
||||
|
||||
echo -e "${CYAN}External Access (LAN: $local_ip)${NC}"
|
||||
echo " Nostr Relay: ws://${local_ip}:7777"
|
||||
echo " Withdraw API: http://${local_ip}:1777"
|
||||
echo ""
|
||||
|
||||
echo -e "${CYAN}lnd-4 (Lightning.Pub backend)${NC}"
|
||||
echo " Balance: ${lnd4_balance} sats"
|
||||
echo ""
|
||||
|
||||
echo -e "${CYAN}ATM App${NC}"
|
||||
if [[ "$app_id" != "not set" ]]; then
|
||||
echo " App ID: ${app_id:0:16}..."
|
||||
echo " Status: $(get_atm_balance)"
|
||||
else
|
||||
echo " Status: not configured"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
if [[ -n "$lndconnect" ]]; then
|
||||
echo -e "${CYAN}Zeus Wallet (connect to lnd-3 for testing)${NC}"
|
||||
echo -e " ${DIM}$lndconnect${NC}"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
echo -e "${CYAN}Quick Commands${NC}"
|
||||
echo " ./dev.sh logs lightning-pub # View Lightning.Pub logs"
|
||||
echo " ./dev.sh fund 100000 # Fund ATM with 100k sats"
|
||||
echo " ./dev.sh status # Show this info"
|
||||
echo ""
|
||||
echo -e "${BOLD}═══════════════════════════════════════════════════════════════════${NC}"
|
||||
}
|
||||
|
||||
#############################################################################
|
||||
# Main Commands
|
||||
#############################################################################
|
||||
|
||||
cmd_up() {
|
||||
local image="$DEFAULT_IMAGE"
|
||||
local worktree=""
|
||||
local skip_regtest=false
|
||||
local auto_fund=false
|
||||
local fund_amount="$DEFAULT_FUNDING_SATS"
|
||||
|
||||
# Parse arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--image) image="$2"; shift 2 ;;
|
||||
--worktree) worktree="$2"; shift 2 ;;
|
||||
--skip-regtest) skip_regtest=true; shift ;;
|
||||
--fund) auto_fund=true; shift ;;
|
||||
--fund=*) auto_fund=true; fund_amount="${1#*=}"; shift ;;
|
||||
*) shift ;;
|
||||
esac
|
||||
done
|
||||
|
||||
echo ""
|
||||
log "Starting Lamassu development environment..."
|
||||
echo ""
|
||||
|
||||
# 1. Start regtest if needed
|
||||
if [[ "$skip_regtest" != "true" ]]; then
|
||||
start_regtest || exit 1
|
||||
else
|
||||
if ! is_regtest_running; then
|
||||
error "Regtest not running. Remove --skip-regtest or start it manually."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# 2. Build from worktree if specified
|
||||
if [[ -n "$worktree" ]]; then
|
||||
image=$(build_from_worktree "$worktree")
|
||||
fi
|
||||
|
||||
# 3. Check image exists
|
||||
if ! docker image inspect "$image" &>/dev/null; then
|
||||
error "Docker image not found: $image"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " 1. Build from worktree: ./dev.sh up --worktree ~/path/to/lightning-pub"
|
||||
echo " 2. Build manually: docker build -t $image ~/path/to/lightning-pub"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "Using Lightning.Pub image: $image"
|
||||
|
||||
# 4. Start lamassu services
|
||||
export REGTEST_DATA_DIR="$REGTEST_DIR/data"
|
||||
export LIGHTNING_PUB_IMAGE="$image"
|
||||
export HOST_IP=$(get_local_ip)
|
||||
|
||||
log "Starting lamassu services..."
|
||||
docker compose -f "$SCRIPT_DIR/docker-compose.regtest.yml" up -d
|
||||
|
||||
# 5. Wait for Lightning.Pub
|
||||
if ! wait_for_lightning_pub; then
|
||||
error "Failed to start. Check: ./dev.sh logs lightning-pub"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 6. Extract and save Lightning.Pub info
|
||||
local pubkey=$(extract_lightning_pub_info)
|
||||
|
||||
# 7. Update ATM .env
|
||||
update_atm_env "$pubkey"
|
||||
|
||||
# 8. Setup ATM app
|
||||
setup_atm_app || true
|
||||
|
||||
# 9. Auto-fund if requested
|
||||
if [[ "$auto_fund" == "true" ]]; then
|
||||
echo ""
|
||||
log "Auto-funding ATM with $fund_amount sats..."
|
||||
sleep 2 # Give Lightning.Pub a moment to settle
|
||||
cmd_fund "$fund_amount" || warn "Auto-funding failed. Run './dev.sh fund' manually."
|
||||
fi
|
||||
|
||||
# 10. Show status
|
||||
show_status
|
||||
}
|
||||
|
||||
cmd_down() {
|
||||
log "Stopping lamassu services..."
|
||||
docker compose -f "$SCRIPT_DIR/docker-compose.regtest.yml" down 2>/dev/null || true
|
||||
|
||||
if [[ "$1" == "--all" ]]; then
|
||||
stop_regtest
|
||||
fi
|
||||
|
||||
success "Services stopped"
|
||||
}
|
||||
|
||||
cmd_reset() {
|
||||
warn "This will delete all lamassu state (Lightning.Pub identity, ATM app, etc.)"
|
||||
read -p "Continue? [y/N] " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
echo "Aborted"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
log "Stopping services..."
|
||||
docker compose -f "$SCRIPT_DIR/docker-compose.regtest.yml" down -v 2>/dev/null || true
|
||||
|
||||
log "Removing state files..."
|
||||
rm -rf "$STATE_DIR"
|
||||
mkdir -p "$STATE_DIR"
|
||||
|
||||
success "Reset complete. Run './dev.sh up' for fresh start."
|
||||
}
|
||||
|
||||
cmd_logs() {
|
||||
docker compose -f "$SCRIPT_DIR/docker-compose.regtest.yml" logs -f "$@"
|
||||
}
|
||||
|
||||
cmd_status() {
|
||||
if ! docker ps --format '{{.Names}}' | grep -q lamassu-lightning-pub; then
|
||||
error "Services not running. Start with: ./dev.sh up"
|
||||
exit 1
|
||||
fi
|
||||
show_status
|
||||
}
|
||||
|
||||
cmd_fund() {
|
||||
local amount="${1:-$DEFAULT_FUNDING_SATS}"
|
||||
|
||||
if ! is_regtest_running; then
|
||||
error "Regtest not running"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check for app token
|
||||
if [[ ! -f "$APP_TOKEN_FILE" ]]; then
|
||||
error "ATM app not configured. Run './dev.sh up' first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local app_token=$(cat "$APP_TOKEN_FILE")
|
||||
|
||||
log "Creating invoice for $amount sats..."
|
||||
|
||||
# Create invoice for app owner (using unique payer_identifier)
|
||||
local payer_id="funder-$(date +%s)"
|
||||
local response=$(curl -s -X POST "http://localhost:1776/api/app/add/invoice" \
|
||||
-H "Authorization: Bearer $app_token" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"payer_identifier\": \"$payer_id\", \"http_callback_url\": \"\", \"invoice_req\": {\"amountSats\": $amount, \"memo\": \"ATM funding\"}}")
|
||||
|
||||
local invoice=$(echo "$response" | grep -oP '"invoice":"\K[^"]+')
|
||||
|
||||
if [[ -z "$invoice" ]]; then
|
||||
error "Failed to create invoice"
|
||||
echo "Response: $response"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "Paying invoice from lnd-3..."
|
||||
|
||||
# Source regtest helpers and pay
|
||||
if [[ -f "$REGTEST_DIR/docker-scripts.sh" ]]; then
|
||||
(
|
||||
cd "$REGTEST_DIR"
|
||||
source docker-scripts.sh 2>/dev/null
|
||||
lncli-sim 3 payinvoice --force "$invoice"
|
||||
)
|
||||
if [[ $? -eq 0 ]]; then
|
||||
success "Funded ATM with $amount sats"
|
||||
else
|
||||
error "Payment failed"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
# Fallback: try direct docker exec
|
||||
docker exec lnbits-lnd-3-1 lncli --network=regtest --rpcserver=lnd-3:10009 payinvoice --force "$invoice"
|
||||
if [[ $? -eq 0 ]]; then
|
||||
success "Funded ATM with $amount sats"
|
||||
else
|
||||
error "Payment failed. Make sure lnd-3 has funds and channels."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_mine() {
|
||||
local blocks="${1:-1}"
|
||||
if ! is_regtest_running; then
|
||||
error "Regtest not running"
|
||||
exit 1
|
||||
fi
|
||||
log "Mining $blocks block(s)..."
|
||||
docker exec lnbits-bitcoind-1 bitcoin-cli -regtest -generate "$blocks" > /dev/null
|
||||
local height=$(docker exec lnbits-bitcoind-1 bitcoin-cli -regtest getblockcount)
|
||||
success "Mined $blocks block(s) (height: $height)"
|
||||
}
|
||||
|
||||
cmd_atm() {
|
||||
local atm_dir="$PROJECT_DIR/apps/machine"
|
||||
|
||||
if [[ ! -d "$atm_dir" ]]; then
|
||||
error "ATM app not found at $atm_dir"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if services are running
|
||||
if ! docker ps --format '{{.Names}}' | grep -q lamassu-lightning-pub; then
|
||||
warn "Services not running. Start with: ./dev.sh up"
|
||||
read -p "Start services first? [Y/n] " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Nn]$ ]]; then
|
||||
cmd_up
|
||||
fi
|
||||
fi
|
||||
|
||||
log "Starting ATM application..."
|
||||
echo ""
|
||||
echo -e "${CYAN}ATM Mock Mode:${NC}"
|
||||
echo " - Press 'b' to insert a bill (simulates cash insertion)"
|
||||
echo " - Use the UI to complete transactions"
|
||||
echo ""
|
||||
|
||||
cd "$atm_dir" && pnpm dev
|
||||
}
|
||||
|
||||
#############################################################################
|
||||
# Entry Point
|
||||
#############################################################################
|
||||
|
||||
case "${1:-help}" in
|
||||
up|start)
|
||||
shift
|
||||
cmd_up "$@"
|
||||
;;
|
||||
down|stop)
|
||||
shift
|
||||
cmd_down "$@"
|
||||
;;
|
||||
reset)
|
||||
cmd_reset
|
||||
;;
|
||||
logs)
|
||||
shift
|
||||
cmd_logs "$@"
|
||||
;;
|
||||
status|info)
|
||||
cmd_status
|
||||
;;
|
||||
fund)
|
||||
shift
|
||||
cmd_fund "$@"
|
||||
;;
|
||||
mine)
|
||||
shift
|
||||
cmd_mine "$@"
|
||||
;;
|
||||
atm)
|
||||
cmd_atm
|
||||
;;
|
||||
*)
|
||||
echo "Lamassu Next Development Environment"
|
||||
echo ""
|
||||
echo "Usage: $0 <command> [options]"
|
||||
echo ""
|
||||
echo "Commands:"
|
||||
echo " up [options] Start development environment"
|
||||
echo " down [--all] Stop services (--all includes regtest)"
|
||||
echo " atm Launch ATM application (Electron)"
|
||||
echo " status Show connection info"
|
||||
echo " logs [service] Follow service logs"
|
||||
echo " fund [sats] Fund ATM account"
|
||||
echo " mine [blocks] Mine blocks manually (default: 1)"
|
||||
echo " reset Delete all state and start fresh"
|
||||
echo ""
|
||||
echo "Note: Auto-miner runs in background (1 block/2min). Check with:"
|
||||
echo " ./dev.sh logs miner"
|
||||
echo ""
|
||||
echo "Options for 'up':"
|
||||
echo " --worktree <path> Build Lightning.Pub from git worktree"
|
||||
echo " --image <name> Use specific Docker image"
|
||||
echo " --skip-regtest Don't auto-start regtest"
|
||||
echo " --fund Auto-fund ATM with 100k sats after startup"
|
||||
echo " --fund=<sats> Auto-fund ATM with specific amount"
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " $0 up # Start with default image"
|
||||
echo " $0 up --fund # Start and fund ATM"
|
||||
echo " $0 up --fund=50000 # Start and fund with 50k sats"
|
||||
echo " $0 up --worktree ~/dev/lightning-pub/withdraw"
|
||||
echo " $0 atm # Launch ATM app"
|
||||
echo " $0 fund 200000 # Add 200k more sats"
|
||||
echo " $0 logs lightning-pub"
|
||||
echo " $0 reset && $0 up --fund # Fresh start with funding"
|
||||
;;
|
||||
esac
|
||||
226
docker/docker-compose.dev.yml
Normal file
226
docker/docker-compose.dev.yml
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
# Lamassu Next Development Infrastructure
|
||||
# Usage: docker compose -f docker-compose.dev.yml up -d
|
||||
|
||||
services:
|
||||
# Private Nostr relay for ATM communication
|
||||
strfry:
|
||||
image: ghcr.io/hoytech/strfry:latest
|
||||
container_name: lamassu-relay
|
||||
ports:
|
||||
- '7777:7777'
|
||||
volumes:
|
||||
- ./strfry.conf:/etc/strfry.conf:ro
|
||||
- strfry-data:/app/strfry-db
|
||||
ulimits:
|
||||
nofile:
|
||||
soft: 524288
|
||||
hard: 524288
|
||||
healthcheck:
|
||||
test: ['CMD', 'nc', '-z', 'localhost', '7777']
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
restart: unless-stopped
|
||||
|
||||
# Bitcoin Core in regtest mode
|
||||
bitcoind:
|
||||
image: lncm/bitcoind:v27.0
|
||||
container_name: lamassu-bitcoind
|
||||
volumes:
|
||||
- bitcoind-data:/data/.bitcoin
|
||||
environment:
|
||||
BITCOIN_NETWORK: regtest
|
||||
command:
|
||||
- -regtest
|
||||
- -server
|
||||
- -rpcuser=lamassu
|
||||
- -rpcpassword=lamassu
|
||||
- -rpcallowip=0.0.0.0/0
|
||||
- -rpcbind=0.0.0.0
|
||||
- -zmqpubrawblock=tcp://0.0.0.0:28332
|
||||
- -zmqpubrawtx=tcp://0.0.0.0:28333
|
||||
- -fallbackfee=0.00001
|
||||
- -txindex=1
|
||||
ports:
|
||||
- '18443:18443' # RPC
|
||||
- '28332:28332' # ZMQ blocks
|
||||
- '28333:28333' # ZMQ tx
|
||||
healthcheck:
|
||||
test:
|
||||
[
|
||||
'CMD',
|
||||
'bitcoin-cli',
|
||||
'-regtest',
|
||||
'-rpcuser=lamassu',
|
||||
'-rpcpassword=lamassu',
|
||||
'getblockchaininfo',
|
||||
]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
restart: unless-stopped
|
||||
|
||||
# LND Lightning node
|
||||
lnd:
|
||||
image: lightninglabs/lnd:v0.18.0-beta
|
||||
container_name: lamassu-lnd
|
||||
depends_on:
|
||||
bitcoind:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- lnd-data:/root/.lnd
|
||||
environment:
|
||||
- NETWORK=regtest
|
||||
command:
|
||||
- --bitcoin.active
|
||||
- --bitcoin.regtest
|
||||
- --bitcoin.node=bitcoind
|
||||
- --bitcoind.rpchost=bitcoind:18443
|
||||
- --bitcoind.rpcuser=lamassu
|
||||
- --bitcoind.rpcpass=lamassu
|
||||
- --bitcoind.zmqpubrawblock=tcp://bitcoind:28332
|
||||
- --bitcoind.zmqpubrawtx=tcp://bitcoind:28333
|
||||
- --rpclisten=0.0.0.0:10009
|
||||
- --restlisten=0.0.0.0:8080
|
||||
- --tlsextradomain=lnd
|
||||
- --tlsextraip=0.0.0.0
|
||||
- --noseedbackup
|
||||
- --accept-keysend
|
||||
- --accept-amp
|
||||
ports:
|
||||
- '10009:10009' # gRPC
|
||||
- '8080:8080' # REST
|
||||
- '9735:9735' # P2P
|
||||
healthcheck:
|
||||
test: ['CMD', 'lncli', '--network=regtest', 'getinfo']
|
||||
interval: 10s
|
||||
timeout: 10s
|
||||
retries: 30
|
||||
start_period: 30s
|
||||
restart: unless-stopped
|
||||
|
||||
# Lightning.Pub - Nostr-native Lightning account system
|
||||
# Note: Lightning.Pub connects to LND for actual Lightning operations
|
||||
# Using patched image with Kind 0 profile publishing support
|
||||
lightning-pub:
|
||||
image: lightning-pub-patched:latest
|
||||
container_name: lamassu-lightning-pub
|
||||
depends_on:
|
||||
lnd:
|
||||
condition: service_healthy
|
||||
extra_hosts:
|
||||
- 'host.docker.internal:host-gateway' # Enable host.docker.internal on Linux
|
||||
ports:
|
||||
- '1776:1776'
|
||||
volumes:
|
||||
- lightning-pub-data:/root/lightning_pub
|
||||
- lnd-data:/root/.lnd:ro # Read-only access to LND data for macaroons/certs
|
||||
environment:
|
||||
- NETWORK=regtest
|
||||
- LND_ADDRESS=lnd:10009
|
||||
- LND_CERT_PATH=/root/.lnd/tls.cert
|
||||
- LND_MACAROON_PATH=/root/.lnd/data/chain/bitcoin/regtest/admin.macaroon
|
||||
# Relay URLs (space-separated). Docker-internal relay FIRST for publishing,
|
||||
# then backup. Mock ATM rewrites ndebit relay for browser access.
|
||||
- NOSTR_RELAYS=ws://strfry:7777
|
||||
# Disable external liquidity provider for regtest
|
||||
- DISABLE_LIQUIDITY_PROVIDER=true
|
||||
# Admin token for HTTP API access (development only)
|
||||
- ADMIN_TOKEN=lamassu-dev-admin-token
|
||||
restart: unless-stopped
|
||||
|
||||
# Second LND node (Alice) for payment testing
|
||||
# This node can pay invoices to Lightning.Pub's LND
|
||||
lnd-alice:
|
||||
image: lightninglabs/lnd:v0.18.0-beta
|
||||
container_name: lamassu-lnd-alice
|
||||
depends_on:
|
||||
bitcoind:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- lnd-alice-data:/root/.lnd
|
||||
environment:
|
||||
- NETWORK=regtest
|
||||
command:
|
||||
- --bitcoin.active
|
||||
- --bitcoin.regtest
|
||||
- --bitcoin.node=bitcoind
|
||||
- --bitcoind.rpchost=bitcoind:18443
|
||||
- --bitcoind.rpcuser=lamassu
|
||||
- --bitcoind.rpcpass=lamassu
|
||||
- --bitcoind.zmqpubrawblock=tcp://bitcoind:28332
|
||||
- --bitcoind.zmqpubrawtx=tcp://bitcoind:28333
|
||||
- --rpclisten=0.0.0.0:10009
|
||||
- --restlisten=0.0.0.0:8080
|
||||
- --tlsextradomain=lnd-alice
|
||||
- --tlsextraip=0.0.0.0
|
||||
- --noseedbackup
|
||||
- --accept-keysend
|
||||
- --accept-amp
|
||||
- --alias=alice
|
||||
ports:
|
||||
- '10010:10009' # gRPC (different host port)
|
||||
- '8081:8080' # REST (different host port)
|
||||
- '9736:9735' # P2P (different host port)
|
||||
healthcheck:
|
||||
test: ['CMD', 'lncli', '--network=regtest', 'getinfo']
|
||||
interval: 10s
|
||||
timeout: 10s
|
||||
retries: 30
|
||||
start_period: 30s
|
||||
restart: unless-stopped
|
||||
|
||||
# Automatic block miner for regtest (keeps LND synced)
|
||||
# Mines 1 block every 30 seconds
|
||||
miner:
|
||||
image: alpine:latest
|
||||
container_name: lamassu-miner
|
||||
depends_on:
|
||||
bitcoind:
|
||||
condition: service_healthy
|
||||
entrypoint: /bin/sh
|
||||
command:
|
||||
- -c
|
||||
- |
|
||||
apk add --no-cache curl jq
|
||||
echo "Starting auto-miner (1 block every 30 seconds)..."
|
||||
while true; do
|
||||
# Create wallet if not exists
|
||||
curl -s --user lamassu:lamassu --data-binary '{"jsonrpc":"1.0","method":"createwallet","params":["miner"]}' http://bitcoind:18443/ > /dev/null 2>&1
|
||||
# Mine a block
|
||||
ADDR=$$(curl -s --user lamassu:lamassu --data-binary '{"jsonrpc":"1.0","method":"getnewaddress","params":[]}' http://bitcoind:18443/ | jq -r '.result // empty')
|
||||
if [ -n "$$ADDR" ]; then
|
||||
curl -s --user lamassu:lamassu --data-binary "{\"jsonrpc\":\"1.0\",\"method\":\"generatetoaddress\",\"params\":[1,\"$$ADDR\"]}" http://bitcoind:18443/ > /dev/null
|
||||
fi
|
||||
sleep 30
|
||||
done
|
||||
restart: unless-stopped
|
||||
profiles:
|
||||
- mining # Only starts with: docker compose --profile mining up -d
|
||||
|
||||
# PostgreSQL for optional server-side state
|
||||
postgres:
|
||||
image: postgres:16-alpine
|
||||
container_name: lamassu-postgres
|
||||
ports:
|
||||
- '5432:5432'
|
||||
environment:
|
||||
POSTGRES_DB: lamassu_dev
|
||||
POSTGRES_USER: lamassu
|
||||
POSTGRES_PASSWORD: lamassu_dev_password
|
||||
volumes:
|
||||
- postgres-data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ['CMD-SHELL', 'pg_isready -U lamassu -d lamassu_dev']
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
strfry-data:
|
||||
bitcoind-data:
|
||||
lnd-data:
|
||||
lnd-alice-data:
|
||||
lightning-pub-data:
|
||||
postgres-data:
|
||||
148
docker/docker-compose.regtest.yml
Normal file
148
docker/docker-compose.regtest.yml
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
# Lamassu Next - Regtest Integration
|
||||
#
|
||||
# This overlay connects lamassu-next services to the comprehensive regtest
|
||||
# environment at ~/dev/local/docker/regtest
|
||||
#
|
||||
# Usage:
|
||||
# 1. Start the regtest environment:
|
||||
# cd ~/dev/local/docker/regtest && ./start-regtest
|
||||
#
|
||||
# 2. Start lamassu services:
|
||||
# cd lamassu-next/docker && docker compose -f docker-compose.regtest.yml up -d
|
||||
#
|
||||
# 3. Configure apps/machine/.env:
|
||||
# VITE_RELAY_URL=ws://localhost:7777
|
||||
# VITE_EXTENSION_API_URL=http://localhost:1777
|
||||
#
|
||||
# Services:
|
||||
# - strfry: Private Nostr relay (port 7777)
|
||||
# - lightning-pub: Nostr-native Lightning account system (port 1776)
|
||||
# - Uses lnd-4 from regtest as backend
|
||||
# - Withdraw extension on port 1777
|
||||
# - miner: Auto-mines blocks to keep Lightning channels active
|
||||
#
|
||||
|
||||
services:
|
||||
# Private Nostr relay for ATM communication
|
||||
strfry:
|
||||
image: ghcr.io/hoytech/strfry:latest
|
||||
container_name: lamassu-relay
|
||||
ports:
|
||||
- '7777:7777'
|
||||
volumes:
|
||||
- ./strfry.conf:/etc/strfry.conf:ro
|
||||
- strfry-data:/app/strfry-db
|
||||
ulimits:
|
||||
nofile:
|
||||
soft: 524288
|
||||
hard: 524288
|
||||
healthcheck:
|
||||
test: ['CMD', 'nc', '-z', 'localhost', '7777']
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- regtest
|
||||
|
||||
# Lightning.Pub - Nostr-native Lightning account system
|
||||
# Connects to lnd-4 from the regtest environment
|
||||
# Use LIGHTNING_PUB_IMAGE env var to specify image (default: lightning-pub-withdraw)
|
||||
lightning-pub:
|
||||
image: ${LIGHTNING_PUB_IMAGE:-lightning-pub-withdraw:latest}
|
||||
container_name: lamassu-lightning-pub
|
||||
extra_hosts:
|
||||
- 'host.docker.internal:host-gateway'
|
||||
ports:
|
||||
- '1776:1776'
|
||||
- '1777:1777' # Withdraw extension HTTP API
|
||||
volumes:
|
||||
- lightning-pub-data:/root/lightning_pub
|
||||
# Override Dockerfile's anonymous /app/data volume with named volume
|
||||
- lightning-pub-appdata:/app/data
|
||||
# Mount lnd-4 data from regtest for macaroons/certs
|
||||
- ${REGTEST_DATA_DIR:-/home/padreug/dev/local/docker/regtest/data}/lnd-4:/root/.lnd:ro
|
||||
environment:
|
||||
- NETWORK=regtest
|
||||
# lnd-4 is accessible via Docker network
|
||||
- LND_ADDRESS=lnd-4:10009
|
||||
- LND_CERT_PATH=/root/.lnd/tls.cert
|
||||
- LND_MACAROON_PATH=/root/.lnd/data/chain/bitcoin/regtest/admin.macaroon
|
||||
# Use strfry from this compose
|
||||
- NOSTR_RELAYS=ws://strfry:7777
|
||||
# Disable external liquidity provider for regtest
|
||||
- DISABLE_LIQUIDITY_PROVIDER=true
|
||||
# Admin token for HTTP API access (development only)
|
||||
- ADMIN_TOKEN=lamassu-dev-admin-token
|
||||
# Extension HTTP API URL (for LNURL callbacks from external wallets)
|
||||
# Use HOST_IP env var for your machine's LAN IP (required for phone wallets)
|
||||
- EXTENSION_SERVICE_URL=http://${HOST_IP:-192.168.1.190}:1777
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- regtest
|
||||
|
||||
# Auto-miner for regtest (mines 1 block every MINE_INTERVAL seconds)
|
||||
# Keeps Lightning channels active during development
|
||||
miner:
|
||||
image: boltz/bitcoin-core:25.0
|
||||
container_name: lamassu-miner
|
||||
entrypoint: /bin/sh
|
||||
command:
|
||||
- -c
|
||||
- |
|
||||
echo "Auto-miner started (interval: $${MINE_INTERVAL}s)"
|
||||
# Wait for bitcoind to be ready
|
||||
while ! bitcoin-cli -regtest -rpcconnect=bitcoind getblockchaininfo > /dev/null 2>&1; do
|
||||
echo "Waiting for bitcoind..."
|
||||
sleep 5
|
||||
done
|
||||
echo "bitcoind ready, starting mining loop"
|
||||
while true; do
|
||||
bitcoin-cli -regtest -rpcconnect=bitcoind -generate 1 > /dev/null 2>&1 && echo "Mined block $(bitcoin-cli -regtest -rpcconnect=bitcoind getblockcount)"
|
||||
sleep $${MINE_INTERVAL}
|
||||
done
|
||||
environment:
|
||||
- MINE_INTERVAL=${MINE_INTERVAL:-120}
|
||||
volumes:
|
||||
- bitcoin-data:/root/.bitcoin
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- regtest
|
||||
|
||||
# PostgreSQL for optional server-side state
|
||||
postgres:
|
||||
image: postgres:16-alpine
|
||||
container_name: lamassu-postgres
|
||||
ports:
|
||||
- '5432:5432'
|
||||
environment:
|
||||
POSTGRES_DB: lamassu_dev
|
||||
POSTGRES_USER: lamassu
|
||||
POSTGRES_PASSWORD: lamassu_dev_password
|
||||
volumes:
|
||||
- postgres-data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ['CMD-SHELL', 'pg_isready -U lamassu -d lamassu_dev']
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- regtest
|
||||
|
||||
volumes:
|
||||
strfry-data:
|
||||
lightning-pub-data:
|
||||
lightning-pub-appdata:
|
||||
postgres-data:
|
||||
# Mount the bitcoin-data volume from the regtest environment
|
||||
bitcoin-data:
|
||||
external: true
|
||||
name: lnbits_bitcoin-data
|
||||
|
||||
networks:
|
||||
regtest:
|
||||
# The regtest environment uses 'lnbits_default' as its Docker network
|
||||
# (named after the original LNbits regtest project)
|
||||
name: lnbits_default
|
||||
external: true
|
||||
335
docker/start-with-regtest.sh
Executable file
335
docker/start-with-regtest.sh
Executable file
|
|
@ -0,0 +1,335 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Start lamassu-next development environment with comprehensive regtest
|
||||
#
|
||||
# This script:
|
||||
# 1. Connects to the regtest environment at ~/dev/local/docker/regtest
|
||||
# 2. Starts Lightning.Pub with the specified image/worktree
|
||||
# 3. Creates and funds an ATM account
|
||||
# 4. Displays connection info for Zeus wallet and Lightning.Pub nprofile
|
||||
#
|
||||
# Prerequisites:
|
||||
# ~/dev/local/docker/regtest must be running:
|
||||
# cd ~/dev/local/docker/regtest && ./start-regtest
|
||||
#
|
||||
# Usage:
|
||||
# ./start-with-regtest.sh # Start with default image
|
||||
# ./start-with-regtest.sh --image myimage # Use specific Docker image
|
||||
# ./start-with-regtest.sh --worktree ~/path # Build from worktree
|
||||
# ./start-with-regtest.sh down # Stop services
|
||||
# ./start-with-regtest.sh logs # Follow logs
|
||||
# ./start-with-regtest.sh status # Show connection info
|
||||
#
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REGTEST_DIR="${REGTEST_DIR:-$HOME/dev/local/docker/regtest}"
|
||||
DEFAULT_IMAGE="lightning-pub-withdraw:latest"
|
||||
ATM_FUNDING_SATS=100000
|
||||
|
||||
# Colors
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
BLUE='\033[0;34m'
|
||||
CYAN='\033[0;36m'
|
||||
BOLD='\033[1m'
|
||||
NC='\033[0m'
|
||||
|
||||
log() { echo -e "${GREEN}[lamassu]${NC} $1"; }
|
||||
warn() { echo -e "${YELLOW}[lamassu]${NC} $1"; }
|
||||
error() { echo -e "${RED}[lamassu]${NC} $1"; }
|
||||
info() { echo -e "${CYAN}[lamassu]${NC} $1"; }
|
||||
|
||||
# Get local IP for external wallet access
|
||||
get_local_ip() {
|
||||
ip route get 1 2>/dev/null | awk '{print $7; exit}' || hostname -I | awk '{print $1}'
|
||||
}
|
||||
|
||||
# Check if regtest is running
|
||||
check_regtest() {
|
||||
if ! docker network inspect lnbits_default &>/dev/null; then
|
||||
error "Regtest network not found!"
|
||||
echo ""
|
||||
echo "Start the regtest environment first:"
|
||||
echo " cd $REGTEST_DIR && ./start-regtest"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if lnd-4 is running (Lightning.Pub's backend)
|
||||
if ! docker exec lnbits-lnd-4-1 lncli --network=regtest getinfo &>/dev/null 2>&1; then
|
||||
warn "lnd-4 not responding. Waiting..."
|
||||
sleep 5
|
||||
fi
|
||||
}
|
||||
|
||||
# Build Lightning.Pub from worktree
|
||||
build_from_worktree() {
|
||||
local worktree="$1"
|
||||
local image_name="lightning-pub-custom:latest"
|
||||
|
||||
if [[ ! -d "$worktree" ]]; then
|
||||
error "Worktree not found: $worktree"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "Building Lightning.Pub from $worktree..."
|
||||
docker build -t "$image_name" "$worktree"
|
||||
echo "$image_name"
|
||||
}
|
||||
|
||||
# Wait for Lightning.Pub to be ready and get nprofile
|
||||
wait_for_lightning_pub() {
|
||||
log "Waiting for Lightning.Pub to start..."
|
||||
local max_attempts=30
|
||||
local attempt=0
|
||||
|
||||
while [[ $attempt -lt $max_attempts ]]; do
|
||||
if docker logs lamassu-lightning-pub 2>&1 | grep -q "LightningPub listening"; then
|
||||
sleep 2 # Extra time for Nostr middleware
|
||||
return 0
|
||||
fi
|
||||
attempt=$((attempt + 1))
|
||||
sleep 2
|
||||
done
|
||||
|
||||
error "Lightning.Pub failed to start within 60 seconds"
|
||||
docker logs lamassu-lightning-pub 2>&1 | tail -20
|
||||
return 1
|
||||
}
|
||||
|
||||
# Get Lightning.Pub nprofile from logs
|
||||
get_nprofile() {
|
||||
docker logs lamassu-lightning-pub 2>&1 | grep -oP 'nprofile:\s*\K\S+' | tail -1
|
||||
}
|
||||
|
||||
# Get Lightning.Pub pubkey from logs
|
||||
get_pubkey() {
|
||||
docker logs lamassu-lightning-pub 2>&1 | grep -oP 'pubkey:\s*\K[a-f0-9]+' | tail -1
|
||||
}
|
||||
|
||||
# Generate lndconnect URL for Zeus (using lnd-3 which has REST exposed)
|
||||
generate_lndconnect() {
|
||||
local lnd_container="lnbits-lnd-3-1"
|
||||
local lnd_data="$REGTEST_DIR/data/lnd-3"
|
||||
local local_ip=$(get_local_ip)
|
||||
local rest_port=8082 # lnd-3's REST port
|
||||
|
||||
# Get cert and macaroon
|
||||
local cert_path="$lnd_data/tls.cert"
|
||||
local mac_path="$lnd_data/data/chain/bitcoin/regtest/admin.macaroon"
|
||||
|
||||
if [[ ! -f "$cert_path" ]] || [[ ! -f "$mac_path" ]]; then
|
||||
warn "lnd-3 credentials not found. Zeus connection string unavailable."
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Base64url encode (replace + with -, / with _, remove =)
|
||||
local cert_b64=$(base64 -w0 "$cert_path" | tr '+/' '-_' | tr -d '=')
|
||||
local mac_b64=$(base64 -w0 "$mac_path" | tr '+/' '-_' | tr -d '=')
|
||||
|
||||
echo "lndconnect://${local_ip}:${rest_port}?cert=${cert_b64}&macaroon=${mac_b64}"
|
||||
}
|
||||
|
||||
# Create and fund ATM account
|
||||
setup_atm_account() {
|
||||
log "Setting up ATM account..."
|
||||
|
||||
# Create app
|
||||
local app_response=$(curl -s -X POST http://localhost:1776/api/admin/app/add \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer lamassu-dev-admin-token" \
|
||||
-d '{"name":"atm-app","allow_user_creation":true}' 2>/dev/null)
|
||||
|
||||
if echo "$app_response" | grep -q '"status":"OK"'; then
|
||||
local app_id=$(echo "$app_response" | grep -oP '"id":"\K[^"]+')
|
||||
local app_token=$(echo "$app_response" | grep -oP '"auth_token":"\K[^"]+')
|
||||
log "Created ATM app: $app_id"
|
||||
|
||||
# Store app token for later use
|
||||
echo "$app_token" > "$SCRIPT_DIR/.atm-app-token"
|
||||
echo "$app_id" > "$SCRIPT_DIR/.atm-app-id"
|
||||
|
||||
# TODO: Fund the app by creating an invoice and paying from lnd-3
|
||||
# This requires the app to support invoice creation via HTTP API
|
||||
# For now, the app starts with 0 balance
|
||||
|
||||
return 0
|
||||
else
|
||||
warn "Failed to create ATM app: $app_response"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Display connection information
|
||||
show_connection_info() {
|
||||
local nprofile=$(get_nprofile)
|
||||
local pubkey=$(get_pubkey)
|
||||
local local_ip=$(get_local_ip)
|
||||
local lndconnect=$(generate_lndconnect 2>/dev/null || echo "")
|
||||
|
||||
echo ""
|
||||
echo -e "${BOLD}═══════════════════════════════════════════════════════════════${NC}"
|
||||
echo -e "${BOLD} LAMASSU REGTEST ENVIRONMENT ${NC}"
|
||||
echo -e "${BOLD}═══════════════════════════════════════════════════════════════${NC}"
|
||||
echo ""
|
||||
|
||||
echo -e "${CYAN}Lightning.Pub:${NC}"
|
||||
echo -e " Pubkey: ${GREEN}$pubkey${NC}"
|
||||
echo -e " nprofile: ${GREEN}$nprofile${NC}"
|
||||
echo ""
|
||||
|
||||
echo -e "${CYAN}Service URLs:${NC}"
|
||||
echo " Nostr Relay: ws://localhost:7777"
|
||||
echo " Lightning.Pub: http://localhost:1776"
|
||||
echo " Withdraw API: http://localhost:1777"
|
||||
echo " PostgreSQL: localhost:5432"
|
||||
echo ""
|
||||
|
||||
echo -e "${CYAN}External Access (from phone/other devices):${NC}"
|
||||
echo " Nostr Relay: ws://${local_ip}:7777"
|
||||
echo " Withdraw API: http://${local_ip}:1777"
|
||||
echo ""
|
||||
|
||||
if [[ -n "$lndconnect" ]]; then
|
||||
echo -e "${CYAN}Zeus Wallet Connection (lnd-3):${NC}"
|
||||
echo -e " ${GREEN}$lndconnect${NC}"
|
||||
echo ""
|
||||
echo " Scan this with Zeus to connect to lnd-3 for testing payments."
|
||||
echo ""
|
||||
fi
|
||||
|
||||
echo -e "${CYAN}ATM Configuration (apps/machine/.env):${NC}"
|
||||
echo " VITE_RELAY_URL=ws://localhost:7777"
|
||||
echo " VITE_LIGHTNING_PUB_PUBKEY=$pubkey"
|
||||
echo " VITE_EXTENSION_API_URL=http://localhost:1777"
|
||||
echo ""
|
||||
|
||||
echo -e "${CYAN}CLI Helpers:${NC}"
|
||||
echo " source $REGTEST_DIR/docker-scripts.sh"
|
||||
echo " bitcoin-cli-sim -generate 1 # Mine blocks"
|
||||
echo " lncli-sim 4 getinfo # lnd-4 (Lightning.Pub)"
|
||||
echo " lncli-sim 3 getinfo # lnd-3 (Zeus wallet)"
|
||||
echo ""
|
||||
echo -e "${BOLD}═══════════════════════════════════════════════════════════════${NC}"
|
||||
}
|
||||
|
||||
# Start services
|
||||
start() {
|
||||
local image="$DEFAULT_IMAGE"
|
||||
local worktree=""
|
||||
|
||||
# Parse arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--image)
|
||||
image="$2"
|
||||
shift 2
|
||||
;;
|
||||
--worktree)
|
||||
worktree="$2"
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
log "Checking prerequisites..."
|
||||
check_regtest
|
||||
|
||||
# Build from worktree if specified
|
||||
if [[ -n "$worktree" ]]; then
|
||||
image=$(build_from_worktree "$worktree")
|
||||
fi
|
||||
|
||||
# Check if image exists
|
||||
if ! docker image inspect "$image" &>/dev/null; then
|
||||
error "Docker image not found: $image"
|
||||
echo ""
|
||||
echo "Either:"
|
||||
echo " 1. Build the image: docker build -t $image <path>"
|
||||
echo " 2. Use --worktree: ./start-with-regtest.sh --worktree ~/path/to/lightning-pub"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "Using Lightning.Pub image: $image"
|
||||
|
||||
# Export environment variables
|
||||
export REGTEST_DATA_DIR="$REGTEST_DIR/data"
|
||||
export LIGHTNING_PUB_IMAGE="$image"
|
||||
export HOST_IP=$(get_local_ip)
|
||||
|
||||
log "Starting lamassu services..."
|
||||
docker compose -f "$SCRIPT_DIR/docker-compose.regtest.yml" up -d
|
||||
|
||||
# Wait for Lightning.Pub and setup
|
||||
if wait_for_lightning_pub; then
|
||||
setup_atm_account || true
|
||||
show_connection_info
|
||||
else
|
||||
error "Failed to start Lightning.Pub. Check logs with: $0 logs lightning-pub"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Stop services
|
||||
stop() {
|
||||
log "Stopping lamassu services..."
|
||||
docker compose -f "$SCRIPT_DIR/docker-compose.regtest.yml" down
|
||||
rm -f "$SCRIPT_DIR/.atm-app-token" "$SCRIPT_DIR/.atm-app-id"
|
||||
log "Services stopped."
|
||||
}
|
||||
|
||||
# Show logs
|
||||
logs() {
|
||||
docker compose -f "$SCRIPT_DIR/docker-compose.regtest.yml" logs -f "$@"
|
||||
}
|
||||
|
||||
# Show status/connection info
|
||||
status() {
|
||||
if ! docker ps --format '{{.Names}}' | grep -q lamassu-lightning-pub; then
|
||||
error "Services not running. Start with: $0 start"
|
||||
exit 1
|
||||
fi
|
||||
show_connection_info
|
||||
}
|
||||
|
||||
# Main
|
||||
case "${1:-start}" in
|
||||
start)
|
||||
shift || true
|
||||
start "$@"
|
||||
;;
|
||||
stop|down)
|
||||
stop
|
||||
;;
|
||||
logs)
|
||||
shift
|
||||
logs "$@"
|
||||
;;
|
||||
status|info)
|
||||
status
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 [command] [options]"
|
||||
echo ""
|
||||
echo "Commands:"
|
||||
echo " start Start services (default)"
|
||||
echo " stop, down Stop services"
|
||||
echo " logs [service] Follow logs"
|
||||
echo " status, info Show connection info"
|
||||
echo ""
|
||||
echo "Options for 'start':"
|
||||
echo " --image <name> Use specific Docker image"
|
||||
echo " --worktree <path> Build from Lightning.Pub worktree"
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " $0 # Start with default image"
|
||||
echo " $0 --worktree ~/dev/lightning-pub/withdraw # Build and use worktree"
|
||||
echo " $0 --image lightning-pub-custom:v1 # Use specific image"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
65
docker/strfry.conf
Normal file
65
docker/strfry.conf
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
##
|
||||
## strfry configuration for Lamassu ATM development
|
||||
##
|
||||
|
||||
relay {
|
||||
bind = "0.0.0.0"
|
||||
port = 7777
|
||||
|
||||
# Set to 0 to skip configuring nofiles limit (avoids container ulimit issues)
|
||||
nofiles = 0
|
||||
|
||||
info {
|
||||
name = "Lamassu Dev Relay"
|
||||
description = "Private Nostr relay for ATM development and testing"
|
||||
pubkey = ""
|
||||
contact = ""
|
||||
}
|
||||
|
||||
# Maximum message size in bytes
|
||||
maxWebsocketPayloadSize = 131072
|
||||
|
||||
# Connection limits
|
||||
maxWebsockets = 100
|
||||
maxConnections = 1000
|
||||
}
|
||||
|
||||
# Event policies
|
||||
events {
|
||||
# Maximum event size
|
||||
maxEventSize = 65536
|
||||
|
||||
# Rate limiting
|
||||
rejectEventsNewerThanSeconds = 900
|
||||
rejectEventsOlderThanSeconds = 94608000
|
||||
|
||||
# Event kinds we care about
|
||||
# 14: Private DMs (NIP-17)
|
||||
# 21000: Lightning.Pub RPC (generic request/response)
|
||||
# 21001-21003: CLINK events (Offer, Debit, Manage)
|
||||
# 22242: NIP-42 auth
|
||||
# 30078: Service Beacon (replaceable, service discovery)
|
||||
# 30079: Transaction records (replaceable)
|
||||
|
||||
# TODO: Review if these ephemeral event settings actually do anything useful.
|
||||
# CLINK events (21000-21003) are in the ephemeral range but need to persist
|
||||
# long enough for request/response flows. These settings may not be recognized
|
||||
# by strfry - verify against strfry documentation.
|
||||
# Allow ephemeral events up to 5 minutes old to be accepted
|
||||
rejectEphemeralEventsOlderThanSeconds = 300
|
||||
# Keep ephemeral events for 5 minutes before deletion
|
||||
ephemeralEventsLifetimeSeconds = 300
|
||||
}
|
||||
|
||||
# Negentropy sync (for relay federation)
|
||||
negentropy {
|
||||
enabled = true
|
||||
syncOnConnect = false
|
||||
}
|
||||
|
||||
# Plugins (for NIP-42 auth in production)
|
||||
# plugins {
|
||||
# authRequired = true
|
||||
# writePolicy = "accept"
|
||||
# readPolicy = "accept"
|
||||
# }
|
||||
Loading…
Add table
Add a link
Reference in a new issue