#!/bin/bash set -e # Exit on any error trap 'echo -e "${RED}An error occurred. Installation aborted.${NOCOLOR}"; exit 1' ERR # Color codes RED='\033[0;31m' GREEN='\033[0;32m' CYAN='\033[0;36m' BLUE='\033[38;2;2;128;175m' YELLOW='\033[1;33m' NOCOLOR='\033[0m' # Default values INSTALL_DIR="/opt/debros" REPO_URL="https://git.debros.io/DeBros/network.git" MIN_GO_VERSION="1.19" BOOTSTRAP_PORT="4001" NODE_PORT="4002" RQLITE_BOOTSTRAP_PORT="5001" RQLITE_NODE_PORT="5002" RAFT_BOOTSTRAP_PORT="7001" RAFT_NODE_PORT="7002" UPDATE_MODE=false log() { echo -e "${CYAN}[$(date '+%Y-%m-%d %H:%M:%S')]${NOCOLOR} $1" } error() { echo -e "${RED}[ERROR]${NOCOLOR} $1" } success() { echo -e "${GREEN}[SUCCESS]${NOCOLOR} $1" } warning() { echo -e "${YELLOW}[WARNING]${NOCOLOR} $1" } # Check if running as root if [[ $EUID -eq 0 ]]; then error "This script should not be run as root. Please run as a regular user with sudo privileges." exit 1 fi # Check if sudo is available if ! command -v sudo &>/dev/null; then error "sudo command not found. Please ensure you have sudo privileges." exit 1 fi # Detect OS detect_os() { if [ -f /etc/os-release ]; then . /etc/os-release OS=$ID VERSION=$VERSION_ID else error "Cannot detect operating system" exit 1 fi case $OS in ubuntu|debian) PACKAGE_MANAGER="apt" ;; centos|rhel|fedora) PACKAGE_MANAGER="yum" if command -v dnf &> /dev/null; then PACKAGE_MANAGER="dnf" fi ;; *) error "Unsupported operating system: $OS" exit 1 ;; esac log "Detected OS: $OS $VERSION" } # Check if DeBros Network is already installed check_existing_installation() { if [ -d "$INSTALL_DIR" ] && [ -f "$INSTALL_DIR/bin/bootstrap" ] && [ -f "$INSTALL_DIR/bin/node" ]; then log "Found existing DeBros Network installation at $INSTALL_DIR" # Check if services are running BOOTSTRAP_RUNNING=false NODE_RUNNING=false if systemctl is-active --quiet debros-bootstrap.service 2>/dev/null; then BOOTSTRAP_RUNNING=true log "Bootstrap service is currently running" fi if systemctl is-active --quiet debros-node.service 2>/dev/null; then NODE_RUNNING=true log "Node service is currently running" fi echo -e "${YELLOW}Existing installation detected!${NOCOLOR}" echo -e "${CYAN}Options:${NOCOLOR}" echo -e "${CYAN}1) Update existing installation${NOCOLOR}" echo -e "${CYAN}2) Remove and reinstall${NOCOLOR}" echo -e "${CYAN}3) Exit installer${NOCOLOR}" while true; do read -rp "Enter your choice (1, 2, or 3): " EXISTING_CHOICE case $EXISTING_CHOICE in 1) UPDATE_MODE=true log "Will update existing installation" return 0 ;; 2) log "Will remove and reinstall" remove_existing_installation UPDATE_MODE=false return 0 ;; 3) log "Installation cancelled by user" exit 0 ;; *) error "Invalid choice. Please enter 1, 2, or 3." ;; esac done else UPDATE_MODE=false return 0 fi } # Remove existing installation remove_existing_installation() { log "Removing existing installation..." # Stop services if they exist for service in debros-bootstrap debros-node; do if systemctl list-unit-files | grep -q "$service.service"; then log "Stopping $service service..." sudo systemctl stop $service.service 2>/dev/null || true sudo systemctl disable $service.service 2>/dev/null || true sudo rm -f /etc/systemd/system/$service.service fi done sudo systemctl daemon-reload # Remove installation directory if [ -d "$INSTALL_DIR" ]; then sudo rm -rf "$INSTALL_DIR" log "Removed installation directory" fi # Remove debros user if id "debros" &>/dev/null; then sudo userdel debros 2>/dev/null || true log "Removed debros user" fi success "Existing installation removed" } # Check Go installation and version check_go_installation() { if command -v go &> /dev/null; then GO_VERSION=$(go version | awk '{print $3}' | sed 's/go//') log "Found Go version: $GO_VERSION" # Compare versions (simplified) if [ "$(printf '%s\n' "$MIN_GO_VERSION" "$GO_VERSION" | sort -V | head -n1)" = "$MIN_GO_VERSION" ]; then success "Go version is sufficient" return 0 else warning "Go version $GO_VERSION is too old. Minimum required: $MIN_GO_VERSION" return 1 fi else log "Go not found on system" return 1 fi } # Install Go install_go() { log "Installing Go..." case $PACKAGE_MANAGER in apt) sudo apt update sudo apt install -y wget ;; yum|dnf) sudo $PACKAGE_MANAGER install -y wget ;; esac # Download and install Go GO_TARBALL="go1.21.0.linux-amd64.tar.gz" ARCH=$(uname -m) if [ "$ARCH" = "aarch64" ]; then GO_TARBALL="go1.21.0.linux-arm64.tar.gz" fi cd /tmp wget -q "https://golang.org/dl/$GO_TARBALL" sudo rm -rf /usr/local/go sudo tar -C /usr/local -xzf "$GO_TARBALL" # Add Go to system-wide PATH if ! grep -q "/usr/local/go/bin" /etc/environment 2>/dev/null; then echo 'PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/usr/local/go/bin"' | sudo tee /etc/environment > /dev/null fi # Also add to current user's bashrc for compatibility if ! grep -q "/usr/local/go/bin" ~/.bashrc; then echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.bashrc fi # Update current session PATH export PATH=$PATH:/usr/local/go/bin success "Go installed successfully" } # Install system dependencies install_dependencies() { log "Checking system dependencies..." # Check which dependencies are missing MISSING_DEPS=() case $PACKAGE_MANAGER in apt) # Check for required packages for pkg in git make build-essential curl; do if ! dpkg -l | grep -q "^ii $pkg "; then MISSING_DEPS+=($pkg) fi done if [ ${#MISSING_DEPS[@]} -gt 0 ]; then log "Installing missing dependencies: ${MISSING_DEPS[*]}" sudo apt update sudo apt install -y "${MISSING_DEPS[@]}" else success "All system dependencies already installed" fi ;; yum|dnf) # Check for required packages for pkg in git make curl; do if ! rpm -q $pkg &>/dev/null; then MISSING_DEPS+=($pkg) fi done # Check for development tools if ! rpm -q gcc &>/dev/null; then MISSING_DEPS+=("Development Tools") fi if [ ${#MISSING_DEPS[@]} -gt 0 ]; then log "Installing missing dependencies: ${MISSING_DEPS[*]}" if [[ " ${MISSING_DEPS[*]} " =~ " Development Tools " ]]; then sudo $PACKAGE_MANAGER groupinstall -y "Development Tools" fi # Remove "Development Tools" from array for individual package installation MISSING_DEPS=($(printf '%s\n' "${MISSING_DEPS[@]}" | grep -v "Development Tools")) if [ ${#MISSING_DEPS[@]} -gt 0 ]; then sudo $PACKAGE_MANAGER install -y "${MISSING_DEPS[@]}" fi else success "All system dependencies already installed" fi ;; esac success "System dependencies ready" } # Check port availability check_ports() { local ports=($BOOTSTRAP_PORT $NODE_PORT $RQLITE_BOOTSTRAP_PORT $RQLITE_NODE_PORT $RAFT_BOOTSTRAP_PORT $RAFT_NODE_PORT) for port in "${ports[@]}"; do if sudo netstat -tuln 2>/dev/null | grep -q ":$port " || ss -tuln 2>/dev/null | grep -q ":$port "; then error "Port $port is already in use. Please free it up and try again." exit 1 fi done success "All required ports are available" } # Configuration wizard configuration_wizard() { log "${BLUE}==================================================${NOCOLOR}" log "${GREEN} DeBros Network Configuration Wizard ${NOCOLOR}" log "${BLUE}==================================================${NOCOLOR}" # Node type selection while true; do echo -e "${GREEN}Select node type:${NOCOLOR}" echo -e "${CYAN}1) Bootstrap Node (Network entry point)${NOCOLOR}" echo -e "${CYAN}2) Regular Node (Connects to existing network)${NOCOLOR}" read -rp "Enter your choice (1 or 2): " NODE_TYPE_CHOICE case $NODE_TYPE_CHOICE in 1) NODE_TYPE="bootstrap" break ;; 2) NODE_TYPE="regular" break ;; *) error "Invalid choice. Please enter 1 or 2." ;; esac done # Solana wallet address log "${GREEN}Enter your Solana wallet address to be eligible for node operator rewards:${NOCOLOR}" while true; do read -rp "Solana Wallet Address: " SOLANA_WALLET if [[ -n "$SOLANA_WALLET" && ${#SOLANA_WALLET} -ge 32 ]]; then break else error "Please enter a valid Solana wallet address" fi done # Data directory read -rp "Installation directory [default: $INSTALL_DIR]: " CUSTOM_INSTALL_DIR if [[ -n "$CUSTOM_INSTALL_DIR" ]]; then INSTALL_DIR="$CUSTOM_INSTALL_DIR" fi # Firewall configuration read -rp "Configure firewall automatically? (yes/no) [default: yes]: " CONFIGURE_FIREWALL CONFIGURE_FIREWALL="${CONFIGURE_FIREWALL:-yes}" success "Configuration completed" } # Create user and directories setup_directories() { log "Setting up directories and permissions..." # Create debros user if it doesn't exist if ! id "debros" &>/dev/null; then sudo useradd -r -s /bin/false -d "$INSTALL_DIR" debros log "Created debros user" else log "User 'debros' already exists" fi # Create directory structure sudo mkdir -p "$INSTALL_DIR"/{bin,configs,keys,data,logs} sudo mkdir -p "$INSTALL_DIR/keys/$NODE_TYPE" sudo mkdir -p "$INSTALL_DIR/data/$NODE_TYPE"/{rqlite,storage} # Set ownership first, then permissions sudo chown -R debros:debros "$INSTALL_DIR" sudo chmod 755 "$INSTALL_DIR" sudo chmod 700 "$INSTALL_DIR/keys" sudo chmod 700 "$INSTALL_DIR/keys/$NODE_TYPE" # Ensure the debros user can write to the keys directory sudo chmod 755 "$INSTALL_DIR/data" sudo chmod 755 "$INSTALL_DIR/logs" sudo chmod 755 "$INSTALL_DIR/configs" sudo chmod 755 "$INSTALL_DIR/bin" success "Directory structure ready" } # Clone or update repository setup_source_code() { log "Setting up source code..." if [ -d "$INSTALL_DIR/src" ]; then log "Updating existing repository..." cd "$INSTALL_DIR/src" sudo -u debros git pull else log "Cloning repository..." sudo -u debros git clone "$REPO_URL" "$INSTALL_DIR/src" cd "$INSTALL_DIR/src" fi success "Source code ready" } # Generate identity key generate_identity() { local identity_file="$INSTALL_DIR/keys/$NODE_TYPE/identity.key" if [ -f "$identity_file" ]; then if [ "$UPDATE_MODE" = true ]; then log "Identity key already exists, keeping existing key" success "Using existing node identity" return 0 else log "Identity key already exists, regenerating..." sudo rm -f "$identity_file" fi fi log "Generating node identity..." cd "$INSTALL_DIR/src" # Create a custom identity generation script with output path support cat > /tmp/generate_identity_custom.go << 'EOF' package main import ( "crypto/rand" "flag" "fmt" "os" "path/filepath" "github.com/libp2p/go-libp2p/core/crypto" "github.com/libp2p/go-libp2p/core/peer" ) func main() { var outputPath string flag.StringVar(&outputPath, "output", "", "Output path for identity key") flag.Parse() if outputPath == "" { fmt.Println("Usage: go run generate_identity_custom.go -output ") os.Exit(1) } // Generate identity priv, pub, err := crypto.GenerateKeyPairWithReader(crypto.Ed25519, 2048, rand.Reader) if err != nil { panic(err) } // Get peer ID peerID, err := peer.IDFromPublicKey(pub) if err != nil { panic(err) } // Marshal private key data, err := crypto.MarshalPrivateKey(priv) if err != nil { panic(err) } // Create directory if err := os.MkdirAll(filepath.Dir(outputPath), 0700); err != nil { panic(err) } // Save identity if err := os.WriteFile(outputPath, data, 0600); err != nil { panic(err) } fmt.Printf("Generated Peer ID: %s\n", peerID.String()) fmt.Printf("Identity saved to: %s\n", outputPath) } EOF # Ensure Go is in PATH and generate the identity key export PATH=$PATH:/usr/local/go/bin sudo -u debros env "PATH=$PATH:/usr/local/go/bin" "GOMOD=$(pwd)" go run /tmp/generate_identity_custom.go -output "$identity_file" rm /tmp/generate_identity_custom.go success "Node identity generated" } # Build binaries build_binaries() { log "Building DeBros Network binaries..." cd "$INSTALL_DIR/src" # Ensure Go is in PATH and build all binaries export PATH=$PATH:/usr/local/go/bin sudo -u debros env "PATH=$PATH:/usr/local/go/bin" make build # Copy binaries to installation directory sudo cp bin/* "$INSTALL_DIR/bin/" sudo chown debros:debros "$INSTALL_DIR/bin/"* success "Binaries built and installed" } # Generate configuration files generate_configs() { log "Generating configuration files..." if [ "$NODE_TYPE" = "bootstrap" ]; then cat > /tmp/config.yaml << EOF node: data_dir: "$INSTALL_DIR/data/bootstrap" key_file: "$INSTALL_DIR/keys/bootstrap/identity.key" listen_addresses: - "/ip4/0.0.0.0/tcp/$BOOTSTRAP_PORT" solana_wallet: "$SOLANA_WALLET" database: rqlite_port: $RQLITE_BOOTSTRAP_PORT rqlite_raft_port: $RAFT_BOOTSTRAP_PORT logging: level: "info" file: "$INSTALL_DIR/logs/bootstrap.log" EOF else cat > /tmp/config.yaml << EOF node: data_dir: "$INSTALL_DIR/data/node" key_file: "$INSTALL_DIR/keys/node/identity.key" listen_addresses: - "/ip4/0.0.0.0/tcp/$NODE_PORT" solana_wallet: "$SOLANA_WALLET" database: rqlite_port: $RQLITE_NODE_PORT rqlite_raft_port: $RAFT_NODE_PORT logging: level: "info" file: "$INSTALL_DIR/logs/node.log" EOF fi sudo mv /tmp/config.yaml "$INSTALL_DIR/configs/$NODE_TYPE.yaml" sudo chown debros:debros "$INSTALL_DIR/configs/$NODE_TYPE.yaml" success "Configuration files generated" } # Configure firewall configure_firewall() { if [[ "$CONFIGURE_FIREWALL" == "yes" ]]; then log "Configuring firewall..." if command -v ufw &> /dev/null; then if [ "$NODE_TYPE" = "bootstrap" ]; then sudo ufw allow $BOOTSTRAP_PORT sudo ufw allow $RQLITE_BOOTSTRAP_PORT sudo ufw allow $RAFT_BOOTSTRAP_PORT else sudo ufw allow $NODE_PORT sudo ufw allow $RQLITE_NODE_PORT sudo ufw allow $RAFT_NODE_PORT fi # Enable ufw if not already active UFW_STATUS=$(sudo ufw status | grep -o "Status: [a-z]*" | awk '{print $2}' || echo "inactive") if [[ "$UFW_STATUS" != "active" ]]; then echo "y" | sudo ufw enable fi success "Firewall configured" else warning "UFW not found. Please configure firewall manually." fi fi } # Create systemd service create_systemd_service() { local service_file="/etc/systemd/system/debros-$NODE_TYPE.service" # Always clean up any existing service files to ensure fresh start for service in debros-bootstrap debros-node; do if [ -f "/etc/systemd/system/$service.service" ]; then log "Cleaning up existing $service service..." sudo systemctl stop $service.service 2>/dev/null || true sudo systemctl disable $service.service 2>/dev/null || true sudo rm -f /etc/systemd/system/$service.service fi done sudo systemctl daemon-reload log "Creating new systemd service..." # Determine the correct ExecStart command based on node type local exec_start="" if [ "$NODE_TYPE" = "bootstrap" ]; then exec_start="$INSTALL_DIR/bin/bootstrap -data $INSTALL_DIR/data/bootstrap -port $BOOTSTRAP_PORT" else # For regular nodes, we need to specify the bootstrap peer # This should be configured based on the bootstrap node's address exec_start="$INSTALL_DIR/bin/node -data $INSTALL_DIR/data/node -port $NODE_PORT" # Note: Bootstrap peer address would need to be configured separately # exec_start="$INSTALL_DIR/bin/node -data $INSTALL_DIR/data/node -port $NODE_PORT -bootstrap /ip4/BOOTSTRAP_IP/tcp/$BOOTSTRAP_PORT/p2p/BOOTSTRAP_PEER_ID" fi cat > /tmp/debros-$NODE_TYPE.service << EOF [Unit] Description=DeBros Network $NODE_TYPE Node After=network.target Wants=network-online.target [Service] Type=simple User=debros Group=debros WorkingDirectory=$INSTALL_DIR ExecStart=$exec_start Restart=always RestartSec=10 StandardOutput=journal StandardError=journal SyslogIdentifier=debros-$NODE_TYPE # Security settings NoNewPrivileges=yes PrivateTmp=yes ProtectSystem=strict ProtectHome=yes ReadWritePaths=$INSTALL_DIR [Install] WantedBy=multi-user.target EOF sudo mv /tmp/debros-$NODE_TYPE.service "$service_file" sudo systemctl daemon-reload sudo systemctl enable debros-$NODE_TYPE.service success "Systemd service ready" } # Start the service start_service() { log "Starting DeBros Network $NODE_TYPE node..." sudo systemctl start debros-$NODE_TYPE.service sleep 3 if systemctl is-active --quiet debros-$NODE_TYPE.service; then success "DeBros Network $NODE_TYPE node started successfully" else error "Failed to start DeBros Network $NODE_TYPE node" log "Check logs with: sudo journalctl -u debros-$NODE_TYPE.service" exit 1 fi } # Display banner display_banner() { echo -e "${BLUE}========================================================================${NOCOLOR}" echo -e "${CYAN} ____ ____ _ _ _ _ | _ \ ___| __ ) _ __ ___ ___ | \ | | ___| |___ _____ _ __| | __ | | | |/ _ \ _ \| __/ _ \/ __| | \| |/ _ \ __\ \ /\ / / _ \| __| |/ / | |_| | __/ |_) | | | (_) \__ \ | |\ | __/ |_ \ V V / (_) | | | < |____/ \___|____/|_| \___/|___/ |_| \_|\___|\__| \_/\_/ \___/|_| |_|\_\\ ${NOCOLOR}" echo -e "${BLUE}========================================================================${NOCOLOR}" } # Main installation function main() { display_banner log "${BLUE}==================================================${NOCOLOR}" log "${GREEN} Starting DeBros Network Installation ${NOCOLOR}" log "${BLUE}==================================================${NOCOLOR}" detect_os check_existing_installation check_ports # Check and install Go if needed if ! check_go_installation; then install_go fi install_dependencies # Skip configuration wizard in update mode if [ "$UPDATE_MODE" != true ]; then configuration_wizard else log "Update mode: skipping configuration wizard" # Detect existing node type if [ -f "$INSTALL_DIR/configs/bootstrap.yaml" ]; then NODE_TYPE="bootstrap" elif [ -f "$INSTALL_DIR/configs/node.yaml" ]; then NODE_TYPE="node" else error "Cannot determine existing node type" exit 1 fi log "Detected existing node type: $NODE_TYPE" fi setup_directories setup_source_code generate_identity build_binaries # Only generate new configs if not in update mode if [ "$UPDATE_MODE" != true ]; then generate_configs configure_firewall else log "Update mode: keeping existing configuration" fi create_systemd_service start_service # Display completion information log "${BLUE}==================================================${NOCOLOR}" if [ "$UPDATE_MODE" = true ]; then log "${GREEN} Update Complete! ${NOCOLOR}" else log "${GREEN} Installation Complete! ${NOCOLOR}" fi log "${BLUE}==================================================${NOCOLOR}" log "${GREEN}Node Type:${NOCOLOR} ${CYAN}$NODE_TYPE${NOCOLOR}" log "${GREEN}Installation Directory:${NOCOLOR} ${CYAN}$INSTALL_DIR${NOCOLOR}" log "${GREEN}Configuration:${NOCOLOR} ${CYAN}$INSTALL_DIR/configs/$NODE_TYPE.yaml${NOCOLOR}" log "${GREEN}Logs:${NOCOLOR} ${CYAN}$INSTALL_DIR/logs/$NODE_TYPE.log${NOCOLOR}" if [ "$NODE_TYPE" = "bootstrap" ]; then log "${GREEN}Bootstrap Port:${NOCOLOR} ${CYAN}$BOOTSTRAP_PORT${NOCOLOR}" log "${GREEN}RQLite Port:${NOCOLOR} ${CYAN}$RQLITE_BOOTSTRAP_PORT${NOCOLOR}" log "${GREEN}Raft Port:${NOCOLOR} ${CYAN}$RAFT_BOOTSTRAP_PORT${NOCOLOR}" else log "${GREEN}Node Port:${NOCOLOR} ${CYAN}$NODE_PORT${NOCOLOR}" log "${GREEN}RQLite Port:${NOCOLOR} ${CYAN}$RQLITE_NODE_PORT${NOCOLOR}" log "${GREEN}Raft Port:${NOCOLOR} ${CYAN}$RAFT_NODE_PORT${NOCOLOR}" fi log "${BLUE}==================================================${NOCOLOR}" log "${GREEN}Management Commands:${NOCOLOR}" log "${CYAN} - sudo systemctl status debros-$NODE_TYPE${NOCOLOR} (Check status)" log "${CYAN} - sudo systemctl restart debros-$NODE_TYPE${NOCOLOR} (Restart service)" log "${CYAN} - sudo systemctl stop debros-$NODE_TYPE${NOCOLOR} (Stop service)" log "${CYAN} - sudo systemctl start debros-$NODE_TYPE${NOCOLOR} (Start service)" log "${CYAN} - sudo journalctl -u debros-$NODE_TYPE.service -f${NOCOLOR} (View logs)" log "${CYAN} - $INSTALL_DIR/bin/network-cli${NOCOLOR} (Use CLI tools)" log "${BLUE}==================================================${NOCOLOR}" if [ "$UPDATE_MODE" = true ]; then success "DeBros Network $NODE_TYPE node has been updated and is running!" else success "DeBros Network $NODE_TYPE node is now running!" fi log "${CYAN}For documentation visit: https://docs.debros.io${NOCOLOR}" } # Run main function main "$@"