feat(docker): auto-setup Lightning channels for unified regtest mode

Add automatic channel setup to dev.sh for seamless ATM development:

- Add ensure_lnd4_channel() to open channels from lnd-1 hub to lnd-3 and lnd-4
- lnd-4 receives channel for Lightning.Pub invoice creation
- lnd-3 receives channel for paying invoices (ATM funding)
- Auto-fund lnd-1 via mining if balance is low
- Include retry logic for channel opening (5 attempts)
- Mine extra blocks and wait for graph propagation to avoid NO_ROUTE errors
- Show channel balance in status output

This makes the unified mode fully functional from a clean slate:
  ./docker/dev.sh up --fund    # Sets up regtest, channels, and funds ATM
  ./docker/dev.sh atm          # Launch ATM application

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Patrick Mulligan 2026-02-15 17:54:40 -05:00
commit 90eceeafd9

View file

@ -86,18 +86,165 @@ is_lnd4_ready() {
docker exec lnbits-lnd-4-1 lncli --network=regtest --rpcserver=lnd-4:10009 getinfo &>/dev/null 2>&1
}
lnd4_has_channels() {
local channel_count=$(docker exec lnbits-lnd-4-1 lncli --network=regtest --rpcserver=lnd-4:10009 listchannels 2>/dev/null | grep -c '"chan_id"' || echo "0")
[[ "$channel_count" -gt 0 ]]
}
lnd4_has_active_channels() {
docker exec lnbits-lnd-4-1 lncli --network=regtest --rpcserver=lnd-4:10009 listchannels 2>/dev/null | grep -q '"active": true'
}
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"
docker exec lnbits-lnd-4-1 lncli --network=regtest --rpcserver=lnd-4:10009 walletbalance 2>/dev/null | grep -oP '"total_balance":\s*"\K[0-9]+' || echo "0"
}
get_lnd4_channel_balance() {
docker exec lnbits-lnd-4-1 lncli --network=regtest --rpcserver=lnd-4:10009 channelbalance 2>/dev/null | grep -oP '"local_balance"[^}]*"sat":\s*"\K[0-9]+' || echo "0"
}
#############################################################################
# Regtest Management
#############################################################################
is_lnd1_ready() {
# Check if lnd-1 is fully ready (can make RPC calls)
docker exec lnbits-lnd-1-1 lncli --network=regtest --rpcserver=lnd-1:10009 getinfo &>/dev/null 2>&1
}
wait_for_lnd1() {
log "Waiting for lnd-1 to be ready..."
local attempts=0
while ! is_lnd1_ready && [[ $attempts -lt 30 ]]; do
sleep 2
attempts=$((attempts + 1))
done
is_lnd1_ready
}
ensure_lnd4_channel() {
# Ensure lnd-4 has a channel so it can create invoices
# Also ensure lnd-3 has a channel so it can pay invoices (for funding)
# Opens channels from lnd-1 (hub) to both lnd-3 and lnd-4
if lnd4_has_active_channels; then
log "lnd-4 has active channels"
return 0
fi
log "Setting up Lightning channels for ATM..."
# Wait for lnd-1 to be ready (it's the funding hub)
if ! wait_for_lnd1; then
error "lnd-1 failed to start"
return 1
fi
success "lnd-1 is ready"
# Give lnd-1 a moment to fully initialize (getinfo succeeds before all subsystems ready)
sleep 5
# Check if lnd-1 has funds, if not fund it
local lnd1_balance=$(docker exec lnbits-lnd-1-1 lncli --network=regtest --rpcserver=lnd-1:10009 walletbalance 2>/dev/null | grep -oP '"confirmed_balance":\s*"\K[0-9]+' | head -1 || echo "0")
lnd1_balance="${lnd1_balance:-0}"
if [[ "$lnd1_balance" -lt 100000000 ]]; then
log "Funding lnd-1 with mining rewards..."
local lnd1_addr=$(docker exec lnbits-lnd-1-1 lncli --network=regtest --rpcserver=lnd-1:10009 newaddress p2wkh 2>/dev/null | grep -oP '"address":\s*"\K[^"]+')
docker exec lnbits-bitcoind-1 bitcoin-cli -regtest generatetoaddress 110 "$lnd1_addr" &>/dev/null
sleep 3
success "lnd-1 funded"
fi
# Get pubkeys
local lnd4_pubkey=$(docker exec lnbits-lnd-4-1 lncli --network=regtest --rpcserver=lnd-4:10009 getinfo 2>/dev/null | grep -oP '"identity_pubkey":\s*"\K[^"]+')
local lnd3_pubkey=$(docker exec lnbits-lnd-3-1 lncli --network=regtest --rpcserver=lnd-3:10009 getinfo 2>/dev/null | grep -oP '"identity_pubkey":\s*"\K[^"]+')
if [[ -z "$lnd4_pubkey" ]] || [[ -z "$lnd3_pubkey" ]]; then
error "Could not get LND pubkeys"
return 1
fi
# Connect lnd-1 to lnd-3 and lnd-4
log "Connecting nodes..."
docker exec lnbits-lnd-1-1 lncli --network=regtest --rpcserver=lnd-1:10009 \
connect "${lnd3_pubkey}@lnd-3:9735" &>/dev/null || true
docker exec lnbits-lnd-1-1 lncli --network=regtest --rpcserver=lnd-1:10009 \
connect "${lnd4_pubkey}@lnd-4:9735" &>/dev/null || true
# Retry loop for opening channels (server may still be initializing)
local result3="" result4="" attempts=0
while [[ $attempts -lt 5 ]]; do
# Open channel to lnd-3: 10M sats, push 5M so lnd-3 has sending capacity
if [[ -z "$result3" ]] || echo "$result3" | grep -q "still in the process of starting"; then
result3=$(docker exec lnbits-lnd-1-1 lncli --network=regtest --rpcserver=lnd-1:10009 \
openchannel --node_key="$lnd3_pubkey" --local_amt=10000000 --push_amt=5000000 2>&1)
fi
# Open channel to lnd-4: 10M sats, push 5M so lnd-4 has receiving capacity
if [[ -z "$result4" ]] || echo "$result4" | grep -q "still in the process of starting"; then
result4=$(docker exec lnbits-lnd-1-1 lncli --network=regtest --rpcserver=lnd-1:10009 \
openchannel --node_key="$lnd4_pubkey" --local_amt=10000000 --push_amt=5000000 2>&1)
fi
# Check if both succeeded
if echo "$result3" | grep -q "funding_txid" && echo "$result4" | grep -q "funding_txid"; then
break
fi
attempts=$((attempts + 1))
if [[ $attempts -lt 5 ]]; then
log "Waiting for LND to be fully ready... (attempt $attempts/5)"
sleep 5
fi
done
log "Opening channel lnd-1 -> lnd-3..."
log "Opening channel lnd-1 -> lnd-4..."
local channels_opened=false
if echo "$result3" | grep -q "funding_txid" || echo "$result4" | grep -q "funding_txid"; then
channels_opened=true
fi
if [[ "$channels_opened" == "true" ]]; then
log "Mining blocks to confirm channels..."
# Mine 10 blocks to confirm channels and allow graph propagation
local addr=$(docker exec lnbits-bitcoind-1 bitcoin-cli -regtest getnewaddress 2>/dev/null)
docker exec lnbits-bitcoind-1 bitcoin-cli -regtest generatetoaddress 10 "$addr" &>/dev/null
# Wait for channels to become active
local attempts=0
while ! lnd4_has_active_channels && [[ $attempts -lt 20 ]]; do
sleep 2
attempts=$((attempts + 1))
done
if lnd4_has_active_channels; then
# Wait a bit more for graph propagation
log "Waiting for network graph to sync..."
sleep 10
# Mine a few more blocks to trigger announcements
docker exec lnbits-bitcoind-1 bitcoin-cli -regtest generatetoaddress 6 "$addr" &>/dev/null
sleep 5
success "Channels active - lnd-3 and lnd-4 ready"
return 0
else
warn "Channels opened but not yet active, may need more time"
return 0
fi
else
error "Failed to open channels"
echo "lnd-3 result: $result3"
echo "lnd-4 result: $result4"
return 1
fi
}
start_regtest() {
if is_regtest_running; then
log "Regtest network already running"
@ -128,6 +275,9 @@ start_regtest() {
error "lnd-4 failed to start"
return 1
fi
# Ensure lnd-4 has a channel for invoices
ensure_lnd4_channel || warn "Could not setup lnd-4 channel (funding may fail)"
}
stop_regtest() {
@ -336,8 +486,10 @@ show_status() {
echo " Withdraw API: http://${local_ip}:1777"
echo ""
local lnd4_channel_balance=$(get_lnd4_channel_balance)
echo -e "${CYAN}lnd-4 (Lightning.Pub backend)${NC}"
echo " Balance: ${lnd4_balance} sats"
echo " Channel Balance: ${lnd4_channel_balance} sats"
echo " Wallet Balance: ${lnd4_balance} sats"
echo ""
echo -e "${CYAN}ATM App${NC}"
@ -413,6 +565,9 @@ cmd_up() {
echo " Start with: regtest-start"
echo " Or use: ./dev.sh up --standalone"
exit 1
elif [[ "$standalone" != "true" ]]; then
# Regtest already running, but ensure lnd-4 has channels
ensure_lnd4_channel || warn "Could not setup lnd-4 channel (funding may fail)"
fi
# 2. Build from worktree if specified