#!/usr/bin/env bash # Copyright 2024 The Hush Developers # Copyright 2024 The DragonX Developers # Released under the GPLv3 # # Download and apply a DRAGONX blockchain bootstrap. # Safely preserves wallet.dat and configuration files. set -euo pipefail BOOTSTRAP_BASE_URL="https://bootstrap.dragonx.is" BOOTSTRAP_FALLBACK_URL="https://bootstrap2.dragonx.is" BOOTSTRAP_FILE="DRAGONX.zip" CHAIN_NAME="DRAGONX" # DragonX bootstrap signing public key (PEM, openssl-compatible). # WHY: the .md5/.sha256 files are served from the same host as the archive, so they # only detect transmission corruption — a compromised bootstrap server could publish a # malicious archive with matching checksums. A detached signature verified against THIS # embedded public key (shipped in the repo, not downloaded) closes that gap: a bad server # cannot forge a signature without the maintainer's offline private key. # # ROLLOUT: until the maintainer embeds a real key here and publishes DRAGONX.zip.sig, # this stays as the placeholder and signature enforcement is skipped (with a loud warning), # so existing users are unaffected. Once a real key is pasted in, an unsigned/invalid # bootstrap is refused (fail-closed). See util/sign-bootstrap.md for the signing procedure. BOOTSTRAP_PUBKEY_PLACEHOLDER="REPLACE_WITH_DRAGONX_BOOTSTRAP_PUBLIC_KEY_PEM" BOOTSTRAP_PUBKEY="$BOOTSTRAP_PUBKEY_PLACEHOLDER" # Determine data directory if [[ "$OSTYPE" == "darwin"* ]]; then DATADIR="$HOME/Library/Application Support/Hush/$CHAIN_NAME" else DATADIR="$HOME/.hush/$CHAIN_NAME" fi CLI="dragonx-cli" # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # No Color info() { echo -e "${GREEN}[INFO]${NC} $*" >&2; } warn() { echo -e "${YELLOW}[WARN]${NC} $*" >&2; } error() { echo -e "${RED}[ERROR]${NC} $*" >&2; exit 1; } # Find dragonx-cli in PATH or relative to this script find_cli() { if command -v "$CLI" &>/dev/null; then CLI=$(command -v "$CLI") elif [[ -x "$(dirname "$0")/../../src/$CLI" ]]; then CLI="$(dirname "$0")/../../src/$CLI" else CLI="" fi } # Stop the daemon if running stop_daemon() { find_cli if [[ -n "$CLI" ]]; then if "$CLI" getinfo &>/dev/null 2>&1; then info "Stopping DragonX daemon..." "$CLI" stop 2>/dev/null || true # Wait for daemon to exit local tries=0 while "$CLI" getinfo &>/dev/null 2>&1; do sleep 2 tries=$((tries + 1)) if [[ $tries -ge 60 ]]; then error "Daemon did not stop after 120 seconds. Please stop it manually and retry." fi done info "Daemon stopped." else info "Daemon is not running." fi else warn "dragonx-cli not found. Please make sure the daemon is stopped before continuing." read -rp "Is the DragonX daemon stopped? (y/N): " answer if [[ "${answer,,}" != "y" ]]; then error "Please stop the daemon first and run this script again." fi fi } # Files/dirs to preserve (never delete these) PRESERVE_LIST=( "wallet.dat" "DRAGONX.conf" "peers.dat" ) # Remove blockchain data while preserving wallet and config clean_chain_data() { if [[ ! -d "$DATADIR" ]]; then info "Data directory does not exist yet, creating it." mkdir -p "$DATADIR" return fi info "Cleaning blockchain data from $DATADIR ..." # Move preserved files to a temp location local tmpdir tmpdir=$(mktemp -d) for f in "${PRESERVE_LIST[@]}"; do if [[ -e "$DATADIR/$f" ]]; then cp -a "$DATADIR/$f" "$tmpdir/" fi done # Remove blockchain directories local dirs_to_remove=("blocks" "chainstate" "notarizations" "komodo" "db.log" "debug.log" "fee_estimates.dat" "banlist.dat") for d in "${dirs_to_remove[@]}"; do if [[ -e "$DATADIR/$d" ]]; then rm -rf "$DATADIR/$d" fi done # Restore preserved files for f in "${PRESERVE_LIST[@]}"; do if [[ -e "$tmpdir/$f" ]]; then cp -a "$tmpdir/$f" "$DATADIR/" fi done rm -rf "$tmpdir" info "Blockchain data cleaned." } # Download a file via wget or curl (returns non-zero on failure) download_file() { local url="$1" local outfile="$2" if command -v wget &>/dev/null; then wget --progress=bar:force -O "$outfile" "$url" elif command -v curl &>/dev/null; then curl -L --progress-bar -o "$outfile" "$url" else error "Neither wget nor curl found. Please install one and retry." fi } # Try downloading from a given base URL; returns non-zero on failure download_from() { local base_url="$1" local outfile="$DATADIR/$BOOTSTRAP_FILE" local md5file="$DATADIR/${BOOTSTRAP_FILE}.md5" local sha256file="$DATADIR/${BOOTSTRAP_FILE}.sha256" local sigfile="$DATADIR/${BOOTSTRAP_FILE}.sig" info "Downloading bootstrap from $base_url ..." info "This may take a while depending on your connection speed." download_file "$base_url/$BOOTSTRAP_FILE" "$outfile" || return 1 info "Bootstrap download complete." info "Downloading checksums..." download_file "$base_url/${BOOTSTRAP_FILE}.md5" "$md5file" || return 1 download_file "$base_url/${BOOTSTRAP_FILE}.sha256" "$sha256file" || return 1 # Detached signature is optional during rollout (non-fatal if absent); enforcement # is decided in verify_signature() based on whether a real public key is embedded. rm -f "$sigfile" download_file "$base_url/${BOOTSTRAP_FILE}.sig" "$sigfile" || warn "No signature file at $base_url (${BOOTSTRAP_FILE}.sig)" return 0 } # Verify the detached signature of the archive against the embedded release public key. # Fail-closed once a real key is configured; skip (with warning) while the placeholder is in place. verify_signature() { local archive="$1" local sigfile="$2" if [[ "$BOOTSTRAP_PUBKEY" == "$BOOTSTRAP_PUBKEY_PLACEHOLDER" ]]; then warn "Bootstrap signature verification is not yet configured (no maintainer key embedded)." warn "Relying on TLS + checksum integrity only. See util/sign-bootstrap.md." return 0 fi if ! command -v openssl &>/dev/null; then error "openssl is required to verify the bootstrap signature but was not found. Install openssl and retry." fi if [[ ! -s "$sigfile" ]]; then error "Bootstrap signature (${BOOTSTRAP_FILE}.sig) is missing; refusing to use an unsigned bootstrap." fi local pubfile pubfile=$(mktemp) printf '%s\n' "$BOOTSTRAP_PUBKEY" > "$pubfile" if openssl dgst -sha256 -verify "$pubfile" -signature "$sigfile" "$archive" >&2; then rm -f "$pubfile" info "Bootstrap signature verified against embedded DragonX release key." else rm -f "$pubfile" error "Bootstrap signature verification FAILED — the archive is NOT signed by the DragonX release key. Aborting; do not use this bootstrap." fi } # Download the bootstrap and verify checksums download_bootstrap() { local outfile="$DATADIR/$BOOTSTRAP_FILE" local md5file="$DATADIR/${BOOTSTRAP_FILE}.md5" local sha256file="$DATADIR/${BOOTSTRAP_FILE}.sha256" local sigfile="$DATADIR/${BOOTSTRAP_FILE}.sig" if ! download_from "$BOOTSTRAP_BASE_URL"; then warn "Primary download failed, trying fallback $BOOTSTRAP_FALLBACK_URL ..." download_from "$BOOTSTRAP_FALLBACK_URL" || error "Download failed from both primary and fallback servers." fi # Verify checksums info "Verifying checksums..." cd "$DATADIR" if command -v md5sum &>/dev/null; then if md5sum -c "$md5file" >&2; then info "MD5 checksum verified." else error "MD5 checksum verification failed! The download may be corrupted." fi else warn "md5sum not found, skipping MD5 verification." fi if command -v sha256sum &>/dev/null; then if sha256sum -c "$sha256file" >&2; then info "SHA256 checksum verified." else error "SHA256 checksum verification failed! The download may be corrupted." fi else warn "sha256sum not found, skipping SHA256 verification." fi # Verify the cryptographic signature (fail-closed once a release key is embedded). verify_signature "$outfile" "$sigfile" # Clean up checksum + signature files rm -f "$md5file" "$sha256file" "$sigfile" echo "$outfile" } # Extract the bootstrap extract_bootstrap() { local archive="$1" info "Extracting bootstrap..." cd "$DATADIR" # Extract zip, but never overwrite wallet.dat or config unzip -o "$archive" -x 'wallet.dat' '*.conf' || error "Extraction failed. Please install unzip and retry." info "Bootstrap extracted successfully." # Clean up the downloaded archive rm -f "$archive" info "Removed downloaded archive to save disk space." } main() { echo "============================================" echo " DragonX Bootstrap Installer" echo "============================================" echo "" info "Data directory: $DATADIR" echo "" # Step 1: Stop daemon stop_daemon # Step 2: Clean old chain data clean_chain_data # Step 3: Download bootstrap local archive archive=$(download_bootstrap) # Step 4: Extract bootstrap extract_bootstrap "$archive" echo "" info "Bootstrap installation complete!" info "You can now start DragonX with: dragonxd" echo "" } main "$@"