Fix cron job redirection to prevent log duplication and ensure transaction sending commands are handled correctly with error checking. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 418bf6f8-212b-4bb0-a7a5-8231a061da4e Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Event-Id: eebfeaae-fd85-413b-a84e-99224a9b6b98 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/9f85e954-647c-46a5-90a7-396e495a805a/418bf6f8-212b-4bb0-a7a5-8231a061da4e/sPDHkg8 Replit-Helium-Checkpoint-Created: true
192 lines
8.1 KiB
Bash
Executable File
192 lines
8.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# ============================================================
|
|
# Timmy Bitcoin Node — One-shot bootstrap for Ubuntu 22.04 LTS
|
|
# Run as root on a fresh Digital Ocean droplet:
|
|
# bash setup.sh
|
|
# ============================================================
|
|
set -euo pipefail
|
|
|
|
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; NC='\033[0m'
|
|
info() { echo -e "${CYAN}[setup]${NC} $*"; }
|
|
ok() { echo -e "${GREEN}[ok]${NC} $*"; }
|
|
warn() { echo -e "${YELLOW}[warn]${NC} $*"; }
|
|
die() { echo -e "${RED}[error]${NC} $*"; exit 1; }
|
|
|
|
[[ $EUID -ne 0 ]] && die "Run as root: sudo bash setup.sh"
|
|
|
|
# ── 0. Config ────────────────────────────────────────────────
|
|
DATA_DIR="/data"
|
|
INFRA_DIR="/opt/timmy-node"
|
|
RPC_PASS=$(openssl rand -hex 24)
|
|
|
|
info "Starting Timmy node setup..."
|
|
echo ""
|
|
|
|
# ── 1. System packages ───────────────────────────────────────
|
|
info "Updating system packages..."
|
|
apt-get update -qq
|
|
apt-get upgrade -y -qq
|
|
apt-get install -y -qq curl wget git ufw jq openssl
|
|
|
|
# ── 2. Docker ────────────────────────────────────────────────
|
|
info "Installing Docker..."
|
|
if ! command -v docker &>/dev/null; then
|
|
curl -fsSL https://get.docker.com | sh
|
|
systemctl enable docker
|
|
systemctl start docker
|
|
fi
|
|
docker --version
|
|
ok "Docker ready"
|
|
|
|
# ── 3. Tailscale ─────────────────────────────────────────────
|
|
info "Installing Tailscale..."
|
|
if ! command -v tailscale &>/dev/null; then
|
|
curl -fsSL https://tailscale.com/install.sh | sh
|
|
fi
|
|
ok "Tailscale installed — you will authenticate it after this script finishes"
|
|
|
|
# ── 4. Firewall ──────────────────────────────────────────────
|
|
info "Configuring UFW firewall..."
|
|
ufw --force reset
|
|
# Allow Tailscale
|
|
ufw allow in on tailscale0
|
|
# Bitcoin P2P
|
|
ufw allow 8333/tcp comment "Bitcoin P2P"
|
|
# Lightning P2P
|
|
ufw allow 9735/tcp comment "Lightning P2P"
|
|
# SSH via Tailscale only — but keep public SSH open until Tailscale is confirmed
|
|
# (after Tailscale auth, you can run: ufw delete allow 22)
|
|
ufw allow 22/tcp comment "SSH (disable after Tailscale is confirmed)"
|
|
ufw default deny incoming
|
|
ufw default allow outgoing
|
|
ufw --force enable
|
|
ok "Firewall configured"
|
|
|
|
# ── 5. Mount the DO block volume ─────────────────────────────
|
|
info "Setting up data volume..."
|
|
# Digital Ocean attaches block volumes at /dev/sda or /dev/disk/by-id/scsi-...
|
|
# Find the attached volume (should be the largest unmounted disk)
|
|
VOLUME_DEV=$(lsblk -rno NAME,SIZE,MOUNTPOINT | awk '$3=="" && $2~/G/ {print $1}' | grep -v "^sda$" | head -1)
|
|
|
|
if [[ -z "$VOLUME_DEV" ]]; then
|
|
warn "Could not auto-detect a block volume. If you attached a DO Volume,"
|
|
warn "run: lsblk to find it, then: mkfs.ext4 /dev/<name> && mount /dev/<name> /data"
|
|
warn "Then re-run this script or continue manually."
|
|
mkdir -p "$DATA_DIR"
|
|
else
|
|
VOLUME_PATH="/dev/$VOLUME_DEV"
|
|
info "Found volume at $VOLUME_PATH"
|
|
# Format only if not already formatted
|
|
if ! blkid "$VOLUME_PATH" &>/dev/null; then
|
|
info "Formatting $VOLUME_PATH with ext4..."
|
|
mkfs.ext4 -F "$VOLUME_PATH"
|
|
fi
|
|
mkdir -p "$DATA_DIR"
|
|
mount "$VOLUME_PATH" "$DATA_DIR"
|
|
# Persist mount across reboots
|
|
BLKID=$(blkid -s UUID -o value "$VOLUME_PATH")
|
|
if ! grep -q "$BLKID" /etc/fstab; then
|
|
echo "UUID=$BLKID $DATA_DIR ext4 defaults,nofail 0 2" >> /etc/fstab
|
|
fi
|
|
ok "Volume mounted at $DATA_DIR"
|
|
fi
|
|
|
|
# ── 6. Directory structure ───────────────────────────────────
|
|
info "Creating directory structure..."
|
|
mkdir -p \
|
|
"$DATA_DIR/bitcoin" \
|
|
"$DATA_DIR/lnd" \
|
|
"$DATA_DIR/lnbits" \
|
|
"$INFRA_DIR/configs"
|
|
ok "Directories ready"
|
|
|
|
# ── 7. Copy configs & inject RPC password ────────────────────
|
|
info "Installing configs..."
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
|
# Bitcoin config — bitcoinknots/bitcoin image uses /home/bitcoin/.bitcoin inside container
|
|
# which maps to $DATA_DIR/bitcoin on the host via the Docker volume bind
|
|
cp "$SCRIPT_DIR/configs/bitcoin.conf" "$DATA_DIR/bitcoin/bitcoin.conf"
|
|
echo "rpcpassword=$RPC_PASS" >> "$DATA_DIR/bitcoin/bitcoin.conf"
|
|
|
|
# LND config — inject rpcpass
|
|
cp "$SCRIPT_DIR/configs/lnd.conf" "$INFRA_DIR/configs/lnd.conf"
|
|
sed -i "s/# bitcoind.rpcpass is written by setup.sh at runtime/bitcoind.rpcpass=$RPC_PASS/" \
|
|
"$INFRA_DIR/configs/lnd.conf"
|
|
|
|
# Mount lnd.conf into container via docker-compose override
|
|
cat > "$INFRA_DIR/docker-compose.override.yml" <<OVERRIDE
|
|
services:
|
|
lnd:
|
|
volumes:
|
|
- lnd_data:/root/.lnd
|
|
- $INFRA_DIR/configs/lnd.conf:/root/.lnd/lnd.conf:ro
|
|
OVERRIDE
|
|
|
|
# Copy main docker-compose and scripts
|
|
cp "$SCRIPT_DIR/docker-compose.yml" "$INFRA_DIR/docker-compose.yml"
|
|
cp "$SCRIPT_DIR/lnd-init.sh" "$INFRA_DIR/lnd-init.sh"
|
|
cp "$SCRIPT_DIR/sweep.sh" "$INFRA_DIR/sweep.sh"
|
|
cp "$SCRIPT_DIR/ops.sh" "$INFRA_DIR/ops.sh"
|
|
chmod +x "$INFRA_DIR/lnd-init.sh" "$INFRA_DIR/sweep.sh" "$INFRA_DIR/ops.sh"
|
|
|
|
ok "Configs installed"
|
|
|
|
# ── 7b. Install cron jobs ─────────────────────────────────────
|
|
info "Installing cron jobs (daily sweep + daily backup)..."
|
|
# Remove any existing Timmy cron entries before re-adding
|
|
crontab -l 2>/dev/null | grep -v "timmy-node" | crontab - || true
|
|
(crontab -l 2>/dev/null; cat <<'CRON'
|
|
# Timmy Node — auto-sweep hot wallet to cold storage (3am UTC daily)
|
|
# sweep.sh manages its own logging to /var/log/timmy-sweep.log via tee
|
|
0 3 * * * bash /opt/timmy-node/sweep.sh > /dev/null 2>&1
|
|
# Timmy Node — LND channel state backup (4am UTC daily)
|
|
0 4 * * * bash /opt/timmy-node/ops.sh backup >> /var/log/timmy-backup.log 2>&1
|
|
CRON
|
|
) | crontab -
|
|
touch /var/log/timmy-sweep.log /var/log/timmy-backup.log
|
|
ok "Cron jobs installed (sweep 3am, backup 4am UTC)"
|
|
|
|
# ── 8. Save credentials ──────────────────────────────────────
|
|
CREDS_FILE="/root/node-credentials.txt"
|
|
cat > "$CREDS_FILE" <<CREDS
|
|
# Timmy Node Credentials — keep this safe
|
|
# Generated: $(date -u)
|
|
|
|
BITCOIN_RPC_USER=satoshi
|
|
BITCOIN_RPC_PASS=$RPC_PASS
|
|
CREDS
|
|
chmod 600 "$CREDS_FILE"
|
|
ok "Credentials saved to $CREDS_FILE"
|
|
|
|
# ── 9. Start Bitcoin Core ────────────────────────────────────
|
|
info "Starting Bitcoin Core (this begins chain sync — will take days)..."
|
|
cd "$INFRA_DIR"
|
|
docker compose up -d bitcoin
|
|
ok "Bitcoin Core started"
|
|
|
|
# ── 10. Done ─────────────────────────────────────────────────
|
|
echo ""
|
|
echo -e "${GREEN}════════════════════════════════════════${NC}"
|
|
echo -e "${GREEN} Setup complete — next steps:${NC}"
|
|
echo -e "${GREEN}════════════════════════════════════════${NC}"
|
|
echo ""
|
|
echo -e " 1. ${CYAN}Authenticate Tailscale:${NC}"
|
|
echo -e " tailscale up --ssh"
|
|
echo -e " (copy the auth URL, open in browser, log in)"
|
|
echo ""
|
|
echo -e " 2. ${CYAN}Verify Tailscale SSH works${NC} from another device:"
|
|
echo -e " ssh root@<node-name> (via Tailscale MagicDNS)"
|
|
echo -e " Then lock down public SSH:"
|
|
echo -e " ufw delete allow 22"
|
|
echo ""
|
|
echo -e " 3. ${CYAN}Check Bitcoin sync progress:${NC}"
|
|
echo -e " watch -n 10 'docker exec bitcoin bitcoin-cli getblockchaininfo | jq .verificationprogress'"
|
|
echo -e " (will show 0.0 → 1.0 over several days)"
|
|
echo ""
|
|
echo -e " 4. ${CYAN}Once sync reaches ~1.0, initialize LND:${NC}"
|
|
echo -e " bash $INFRA_DIR/lnd-init.sh"
|
|
echo ""
|
|
echo -e " Credentials: ${YELLOW}cat $CREDS_FILE${NC}"
|
|
echo ""
|