From 056d69ae328e24dbc21e65c21fc83b6f4dd282f3 Mon Sep 17 00:00:00 2001 From: Shubham Prajapati Date: Fri, 6 Dec 2024 08:36:38 +0530 Subject: [PATCH] adding soon blockchain --- blockchain/solana/soon/setdata.go | 246 ++++++++++++++ blockchain/solana/soon/setdata.js | 295 ++++++++++++++++ core/client.go | 14 +- install-node.sh | 545 ++++++++++++++++++++++++++++++ util/pkg/node/node.go | 16 + 5 files changed, 1110 insertions(+), 6 deletions(-) create mode 100644 blockchain/solana/soon/setdata.go create mode 100644 blockchain/solana/soon/setdata.js create mode 100644 install-node.sh diff --git a/blockchain/solana/soon/setdata.go b/blockchain/solana/soon/setdata.go new file mode 100644 index 0000000..e188bff --- /dev/null +++ b/blockchain/solana/soon/setdata.go @@ -0,0 +1,246 @@ +// package main + +// import ( +// "bytes" +// "fmt" +// "os" +// "os/exec" +// ) + +// func main() { +// // Path to the JavaScript file +// jsFilePath := "setdata.js" +// projectDir := "." + +// // Your private key in base58 format (replace with your actual private key) +// privateKey := "kRVZ7yFvAZFufaUJheFecbp8gESdEFB6hzwRXzzdJGZdnH6hTvdhhZf65bCfG2fAyb9AxFuw3JTvXDPUPXwA5rG" + +// // Step 1: Check if `@solana/web3.js` is installed +// fmt.Println("Checking if @solana/web3.js is installed...") +// checkPackage := exec.Command("npm", "list", "@solana/web3.js") +// checkPackage.Dir = projectDir +// if err := checkPackage.Run(); err != nil { +// fmt.Println("@solana/web3.js is not installed. Installing now...") +// installPackage := exec.Command("npm", "install", "@solana/web3.js") +// installPackage.Dir = projectDir +// if err := installPackage.Run(); err != nil { +// fmt.Printf("Error installing @solana/web3.js: %s\n", err.Error()) +// return +// } +// fmt.Println("Successfully installed @solana/web3.js.") +// } else { +// fmt.Println("@solana/web3.js is already installed.") +// } + +// // Step 2: Run npm install +// fmt.Println("Running npm install to ensure all dependencies are installed...") +// npmInstall := exec.Command("npm", "install") +// npmInstall.Dir = projectDir +// if err := npmInstall.Run(); err != nil { +// fmt.Printf("Error running npm install: %s\n", err.Error()) +// return +// } +// fmt.Println("All dependencies are installed.") + +// // Step 3: Execute the JavaScript file +// fmt.Println("Executing JavaScript file:", jsFilePath) +// cmd := exec.Command("node", jsFilePath) +// cmd.Dir = projectDir + +// // Set the private key as an environment variable +// cmd.Env = append(os.Environ(), fmt.Sprintf("SOLANA_PRIVATE_KEY=%s", privateKey)) + +// // Buffers to capture the output and errors +// var stdout, stderr bytes.Buffer +// cmd.Stdout = &stdout +// cmd.Stderr = &stderr + +// // Run the command +// err := cmd.Run() + +// // Handle errors and output +// if err != nil { +// fmt.Printf("Error executing JavaScript: %s\n", err.Error()) +// fmt.Printf("Stderr: %s\n", stderr.String()) +// return +// } + +// fmt.Println("JavaScript Output:") +// fmt.Println(stdout.String()) +// } +package soon_solana + +import ( + "bytes" + "fmt" + "io/ioutil" + "os" + "os/exec" + "text/template" +) + +// func main() { +// SoonNodeCreation() +// } + +type NodeDetails struct { + PrivateKey string `json:"private_key"` // Private key in base58 format + PeaqDid string `json:"peaq_did"` // DID (Decentralized Identifier) + NodeName string `json:"nodename"` // Name of the node + IPAddress string `json:"ipaddress"` // IP address of the node + ISPInfo string `json:"ispinfo"` // Information about the ISP + Region string `json:"region"` // Region of the node + Location string `json:"location"` // Geographical location of the node +} + +func SoonNodeCreation(data NodeDetails) { + // Path to the JavaScript file + // jsFilePath := "setdata.js" + jsFilePath := "blockchain/solana/soon/setdata.js" + + // + projectDir := "blockchain/solana/soon/." + + // // Your private key in base58 format (replace with your actual private key) + // privateKey := "kRVZ7yFvAZFufaUJheFecbp8gESdEFB6hzwRXzzdJGZdnH6hTvdhhZf65bCfG2fAyb9AxFuw3JTvXDPUPXwA5rG" + + // Set environment variable to pass to JS file + os.Setenv("SOLANA_PRIVATE_KEY", data.PrivateKey) + + // Node details to pass to JavaScript file (dynamic values) + peaqDid := data.PeaqDid + nodename := data.NodeName + ipaddress := data.IPAddress + ispinfo := data.IPAddress + region := data.Region + location := data.Location + + // Step 1: Check if `@solana/web3.js` is installed + fmt.Println("Checking if @solana/web3.js is installed...") + checkPackage := exec.Command("npm", "list", "@solana/web3.js") + checkPackage.Dir = projectDir + if err := checkPackage.Run(); err != nil { + fmt.Println("@solana/web3.js is not installed. Installing now...") + installPackage := exec.Command("npm", "install", "@solana/web3.js") + installPackage.Dir = projectDir + if err := installPackage.Run(); err != nil { + fmt.Printf("Error installing @solana/web3.js: %s\n", err.Error()) + return + } + fmt.Println("Successfully installed @solana/web3.js.") + } else { + fmt.Println("@solana/web3.js is already installed.") + } + + // Step 2: Check if `@project-serum/anchor` is installed + fmt.Println("Checking if @project-serum/anchor is installed...") + checkAnchorPackage := exec.Command("npm", "list", "@project-serum/anchor") + checkAnchorPackage.Dir = projectDir + if err := checkAnchorPackage.Run(); err != nil { + fmt.Println("@project-serum/anchor is not installed. Installing now...") + installAnchorPackage := exec.Command("npm", "install", "@project-serum/anchor") + installAnchorPackage.Dir = projectDir + if err := installAnchorPackage.Run(); err != nil { + fmt.Printf("Error installing @project-serum/anchor: %s\n", err.Error()) + return + } + fmt.Println("Successfully installed @project-serum/anchor.") + } else { + fmt.Println("@project-serum/anchor is already installed.") + } + + // Step 2: Run npm install + fmt.Println("Running npm install to ensure all dependencies are installed...") + npmInstall := exec.Command("npm", "install") + npmInstall.Dir = projectDir + if err := npmInstall.Run(); err != nil { + fmt.Printf("Error running npm install: %s\n", err.Error()) + return + } + fmt.Println("All dependencies are installed.") + + // Step 3: Inject dynamic data into setdata.js + err := injectDataIntoJS(jsFilePath, peaqDid, nodename, ipaddress, ispinfo, region, location) + if err != nil { + fmt.Printf("Error injecting data into JS file: %s\n", err.Error()) + return + } + + // Step 4: Execute the modified JavaScript file + fmt.Println("Executing data file:", jsFilePath) + cmd := exec.Command("node", jsFilePath) + cmd.Dir = projectDir + + // Buffers to capture the output and errors + var stdout, stderr bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr + + // Run the command + err = cmd.Run() + + // Handle errors and output + if err != nil { + fmt.Printf("Error executing JavaScript: %s\n", err.Error()) + fmt.Printf("Stderr: %s\n", stderr.String()) + return + } + + fmt.Println("Data Output:") + fmt.Println(stdout.String()) +} + +// Function to inject dynamic data into the JS file +func injectDataIntoJS(jsFilePath, peaqDid, nodename, ipaddress, ispinfo, region, location string) error { + // Read the template JS file + jsTemplate, err := ioutil.ReadFile(jsFilePath) + if err != nil { + return fmt.Errorf("failed to read JS file: %w", err) + } + + // Define a template to inject the dynamic values + jsTemplateStr := string(jsTemplate) + tmpl, err := template.New("js").Parse(jsTemplateStr) + if err != nil { + return fmt.Errorf("failed to parse template: %w", err) + } + + // Create a struct to hold dynamic data + data := struct { + PeaqDid string + NodeName string + IPAddr string + ISPInfo string + Region string + Location string + }{ + PeaqDid: peaqDid, + NodeName: nodename, + IPAddr: ipaddress, + ISPInfo: ispinfo, + Region: region, + Location: location, + } + + // Create a temporary JS file with dynamic data injected + tempJsFilePath := jsFilePath + ".temp" + tempFile, err := os.Create(tempJsFilePath) + if err != nil { + return fmt.Errorf("failed to create temporary JS file: %w", err) + } + defer tempFile.Close() + + // Execute the template with dynamic data and write to the temp file + err = tmpl.Execute(tempFile, data) + if err != nil { + return fmt.Errorf("failed to execute template: %w", err) + } + + // Replace the original JS file with the modified one + err = os.Rename(tempJsFilePath, jsFilePath) + if err != nil { + return fmt.Errorf("failed to rename temp JS file: %w", err) + } + + return nil +} diff --git a/blockchain/solana/soon/setdata.js b/blockchain/solana/soon/setdata.js new file mode 100644 index 0000000..33b801c --- /dev/null +++ b/blockchain/solana/soon/setdata.js @@ -0,0 +1,295 @@ +// The following variables will be dynamically injected by the Go code + +const peaqDid = "did:peaq:testnode123"; // Placeholder for dynamic Peaq DID +const nodename = "TestVPNNode"; // Placeholder for dynamic Node Name +const ipaddress = "192.168.1.100"; // Placeholder for dynamic IP Address +const ispinfo = "TestISP"; // Placeholder for dynamic ISP Information +const region = "EU-Central"; // Placeholder for dynamic Region +const location = "Frankfurt, Germany"; // Placeholder for dynamic Location + +// Log the values to check if they're injected correctly +console.log("Peaq DID:", peaqDid); +console.log("Node Name:", nodename); +console.log("IP Address:", ipaddress); +console.log("ISP Info:", ispinfo); +console.log("Region:", region); +console.log("Location:", location); + +const { + Connection, + PublicKey, + Keypair, + SystemProgram, + LAMPORTS_PER_SOL, +} = require("@solana/web3.js"); +const { Program, AnchorProvider, BN } = require("@project-serum/anchor"); +const fs = require("fs"); +const path = require("path"); +const bs58 = require("bs58"); // Import the base58 decoding library + +// Program ID from your deployment +const PROGRAM_ID = new PublicKey( + "3ypCkXQWiAFkNk7bo8bnZFxUVmVEWCqpBoY7v4vgPnHJ" +); + +// Fixed IDL with correct string type definitions +const IDL = { + version: "0.1.0", + name: "erebrus", + instructions: [ + { + name: "registerVpnNode", + accounts: [ + { + name: "vpnNode", + isMut: true, + isSigner: false, + }, + { + name: "user", + isMut: true, + isSigner: true, + }, + { + name: "systemProgram", + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: "userNodeNum", + type: "u64", + }, + { + name: "peaqDid", + type: "string", + }, + { + name: "nodename", + type: "string", + }, + { + name: "ipaddress", + type: "string", + }, + { + name: "ispinfo", + type: "string", + }, + { + name: "region", + type: "string", + }, + { + name: "location", + type: "string", + }, + ], + discriminator: [254, 249, 109, 84, 232, 26, 70, 251], + }, + ], + accounts: [ + { + name: "VpnNode", + type: { + kind: "struct", + fields: [ + { + name: "nodeId", + type: "u64", + }, + { + name: "user", + type: "publicKey", + }, + { + name: "peaqDid", + type: "string", + }, + { + name: "nodename", + type: "string", + }, + { + name: "ipaddress", + type: "string", + }, + { + name: "ispinfo", + type: "string", + }, + { + name: "region", + type: "string", + }, + { + name: "location", + type: "string", + }, + { + name: "status", + type: "u8", + }, + { + name: "canClose", + type: "bool", + }, + ], + }, + discriminator: [154, 255, 245, 194, 44, 120, 114, 244], + }, + ], +}; + +async function checkBalance(connection, publicKey) { + try { + const balance = await connection.getBalance(publicKey); + return balance / LAMPORTS_PER_SOL; + } catch (error) { + console.error("Error checking balance:", error); + return 0; + } +} + +async function registerVpnNode() { + try { + // Connect to devnet + const connection = new Connection( + "https://rpc.devnet.soo.network/rpc", + "confirmed" + ); + + // Load private key from environment variable (base58 string) + const privateKeyEnv = process.env.SOLANA_PRIVATE_KEY; + if (!privateKeyEnv) { + console.error("Error: SOLANA_PRIVATE_KEY environment variable is not set."); + process.exit(1); + } + + // Decode the private key from base58 format to Uint8Array + const privateKey = bs58.decode(privateKeyEnv); + const wallet = Keypair.fromSecretKey(privateKey); + + console.log("Using wallet address:", wallet.publicKey.toString()); + + // Check balance + const balance = await checkBalance(connection, wallet.publicKey); + console.log("Current balance:", balance, "SOL"); + + // Setup provider + const provider = new AnchorProvider( + connection, + { + publicKey: wallet.publicKey, + signTransaction: async (tx) => { + tx.partialSign(wallet); + return tx; + }, + signAllTransactions: async (txs) => { + return txs.map((t) => { + t.partialSign(wallet); + return t; + }); + }, + }, + { commitment: "confirmed" } + ); + + // Create program interface + const program = new Program(IDL, PROGRAM_ID, provider); + + // Generate a node number + // const userNodeNum = new BN(1); + let userNodeNum = new BN(Date.now()); // Timestamp in milliseconds + + // Print the userNodeNum + console.log("Generated userNodeNum (on-chain counter):", userNodeNum.toString()); + + + // Find PDA + const [vpnNodePDA] = PublicKey.findProgramAddressSync( + [Buffer.from("vpn"), userNodeNum.toArrayLike(Buffer, "le", 8)], + program.programId + ); + + console.log("VPN Node PDA:", vpnNodePDA.toString()); + + // Register VPN node + const nodeDetails = { + userNodeNum: userNodeNum, + peaqDid: "did:peaq:testnode123", + nodename: "TestVPNNode", + ipaddress: "192.168.1.100", + ispinfo: "TestISP", + region: "EU-Central", + location: "Frankfurt, Germany", + }; + + const tx = await program.methods + .registerVpnNode( + nodeDetails.userNodeNum, + nodeDetails.peaqDid, + nodeDetails.nodename, + nodeDetails.ipaddress, + nodeDetails.ispinfo, + nodeDetails.region, + nodeDetails.location + ) + .accounts({ + vpnNode: vpnNodePDA, + user: wallet.publicKey, + systemProgram: SystemProgram.programId, + }) + .signers([wallet]) + .rpc(); + + console.log("Transaction signature:", tx); + + // Fetch and display the created account + await connection.confirmTransaction(tx); + const vpnNodeAccount = await program.account.vpnNode.fetch(vpnNodePDA); + + console.log("Created VPN Node Account:", { + nodeId: vpnNodeAccount.nodeId.toString(), + user: vpnNodeAccount.user.toString(), + peaqDid: vpnNodeAccount.peaqDid, + nodename: vpnNodeAccount.nodename, + ipaddress: vpnNodeAccount.ipaddress, + ispinfo: vpnNodeAccount.ispinfo, + region: vpnNodeAccount.region, + location: vpnNodeAccount.location, + status: vpnNodeAccount.status, + canClose: vpnNodeAccount.canClose, + }); + + return { + success: true, + signature: tx, + vpnNodePDA: vpnNodePDA.toString(), + account: vpnNodeAccount, + }; + } catch (error) { + console.error("Error registering VPN node:", error); + if (error.logs) { + console.error("Program logs:", error.logs); + } + return { + success: false, + error: error.message, + logs: error.logs, + }; + } +} + +// Execute the registration +console.log("Starting VPN node registration..."); +registerVpnNode() + .then((result) => { + console.log("Registration result:", result); + process.exit(result.success ? 0 : 1); + }) + .catch((error) => { + console.error("Fatal error:", error); + process.exit(1); + }); \ No newline at end of file diff --git a/core/client.go b/core/client.go index f5d7a63..606e188 100644 --- a/core/client.go +++ b/core/client.go @@ -213,24 +213,26 @@ func ReadClientConfig(id string) ([]byte, error) { return configDataWg, nil } -func GeneratePeaqDID(length int) (string, error) { + +// LENGTH 16 +func GeneratePeaqDID(length int) (string, string, error) { if length <= 0 { - length = 16 + length = 55 } - const validChars = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkm" + const validChars = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" result := make([]byte, length) for i := 0; i < length; i++ { randomIndex, err := rand.Int(rand.Reader, big.NewInt(int64(len(validChars)))) if err != nil { - return "", fmt.Errorf("failed to generate random number: %v", err) + return "", "", fmt.Errorf("failed to generate random number: %v", err) } result[i] = validChars[randomIndex.Int64()] fmt.Println("result : ", result) } - return fmt.Sprintf("did:peaq:%s", string(result)), nil + return fmt.Sprintf("did:peaq:%s", string(result)), string(result), nil } func IsValidPeaqDID(did string) bool { @@ -248,7 +250,7 @@ func IsValidPeaqDID(did string) bool { } // Define the allowed characters for idchar - idcharRegex := regexp.MustCompile(`^[1-9A-HJ-NP-Za-km]+$`) + idcharRegex := regexp.MustCompile(`^[1-9A-HJ-NP-Za-km-z]+$`) // Check if the id-string contains only valid idchar characters return idcharRegex.MatchString(idString) diff --git a/install-node.sh b/install-node.sh new file mode 100644 index 0000000..75f1cb3 --- /dev/null +++ b/install-node.sh @@ -0,0 +1,545 @@ +#!/usr/bin/env bash + +# Function to display header and stage status +display_header() { + clear + brand_color="\e[94m" + reset_color="\e[0m" + echo -e "${brand_color}" + cat << "EOF" + /$$$$$$$$ /$$ +| $$_____/ | $$ +| $$ /$$$$$$ /$$$$$$ | $$$$$$$ /$$$$$$ /$$ /$$ /$$$$$$$ +| $$$$$ /$$__ $$ /$$__ $$| $$__ $$ /$$__ $$| $$ | $$ /$$_____/ +| $$__/ | $$ \__/| $$$$$$$$| $$ \ $$| $$ \__/| $$ | $$| $$$$$$ +| $$ | $$ | $$_____/| $$ | $$| $$ | $$ | $$ \____ $$ +| $$$$$$$$| $$ | $$$$$$$| $$$$$$$/| $$ | $$$$$$/ /$$$$$$$/ +|________/|__/ \_______/|_______/ |__/ \______/ |_______/ + + Powered by NetSepio +EOF + echo -e "${reset_color}" + printf "\n\e[1m\e[4m=== Erebrus Node Software Installer - Version 1.0 ===\e[0m\n" + printf "%0.s=" {1..120} # Print a line separator of 80 characters + printf "\n" + printf "\n\e[1mRequirements:\e[0m\n" + printf "1. Erebrus node needs public IP that is routable from internet.\n" + printf " Node software requires public IP to funtion properly.\n" + printf "2. Ports 9080, 9002 and 51820 must be open on your firewall and/or host system.\n" + printf " Ensure these ports are accessible to run the Erebrus Node software.\n" + printf "%0.s=" {1..120} # Print a line separator of 80 characters + printf "\n" + printf "\n\e[1mStage 1 - Install Dependencies:\e[0m\t [${status_stage1}\e[0m]\n" + printf "\e[1mStage 2 - Configure Node:\e[0m\t [${status_stage2}\e[0m]\n" + printf "\e[1mStage 3 - Run Node:\e[0m\t [${status_stage3}\e[0m]\n\n" +} + +# Function to show spinner +show_spinner() { + local pid=$1 + local delay=0.2 + local spinstr='|/-\' + tput civis + printf " [" + while [ "$(ps a | awk '{print $1}' | grep $pid)" ]; do + local temp=${spinstr#?} + printf "%c]" "$spinstr" + local spinstr=$temp${spinstr%"$temp"} + sleep $delay + printf "\b\b" + done + printf " \t" + tput civis +} + +# Function to check if Docker is installed +is_docker_installed() { + if command -v docker > /dev/null && command -v docker-compose > /dev/null; then + return 0 + else + return 1 + fi +} + +# Check if given group name exists in system +function group_exists() { + if command -v getent >/dev/null 2>&1; then + # Use getent if available (common in Linux) + if getent group "$1" >/dev/null 2>&1; then + return 0 + else + return 1 + fi + # Check using dscl (might be more reliable on macOS) + elif command -v dscl >/dev/null 2>&1; then + if dscl . -list /Groups | grep "$1" >/dev/null 2>&1; then + return 0 + else + return 1 + fi + fi +} + +function create_group() { + # Create group, takes group name as an argument $1 + if command -v groupadd; then + sudo groupadd "$1" + [[ $? -eq 0 ]] && return 0 || return 1 + elif command -v dscl; then + dscl . -create /Groups/"$1" + [[ $? -eq 0 ]] && return 0 || return 1 + fi +} + +#Create docker group and add user to the group +function add_user_to_group() { + # Add current user to docker group + if command -v usermod; then + if ! groups "$USER" | grep "$1"; then + sudo usermod -aG "$1" "$USER" # Use sudo and usermod for Linux + [[ $? -eq 0 ]] && return 0 || return 1 + fi + elif command -v dscl; then + if ! dscl . -read /Groups/"$1" | grep GroupMembership | grep "$USER"; then + dscl . -append /Groups/"$1" GroupMembership "$USER" # Use dscl for macOS + [[ $? -eq 0 ]] && return 0 || return 1 + fi + fi +} + + +# Function to install Docker and Docker Compose based on distribution +install_dependencies() { + clear + status_stage1="\e[34mIn Progress\e[0m" + display_header + printf "\e[1mInstalling Docker...\e[0m" + if is_docker_installed; then + printf " \e[32m[Already installed]\e[0m\n" + sleep 2 + else + if command -v apt-get > /dev/null; then + (sudo apt-get update -qq && sudo apt-get install -y containerd docker.io && sudo apt-get install netcat-* -y && sudo apt-get install lsof -y > /dev/null 2> error.log) & + show_spinner $! + printf " \e[32mComplete\e[0m\n" + elif command -v yum > /dev/null; then + (sudo yum install yum-utils -y && sudo yum install nmap-ncat.x86_64 -y && sudo yum install lsof -y && sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo && yum install -y docker > /dev/null 2>&1 && sudo systemctl start docker && sudo systemctl enable docker > /dev/null 2> error.log) & + show_spinner $! + printf " \e[32mComplete\e[0m\n" + elif command -v pacman > /dev/null; then + (sudo pacman -Sy --noconfirm docker > /dev/null 2>&1 && sudo systemctl start docker && sudo systemctl enable docker > /dev/null 2> error.log) & + show_spinner $! + printf " \e[32mComplete\e[0m\n" + elif command -v dnf > /dev/null; then + printf "Installing Docker on Fedora..." + (sudo dnf install dnf-plugins-core && dnf config-manager --add-repo https://download.docker.com/linux/fedora/docker-ce.repo && dnf install -y docker-ce docker-ce-cli containerd.io > /dev/null 2>&1 && sudo systemctl start docker && sudo systemctl enable docker > /dev/null 2> error.log) & + show_spinner $! + printf " \e[32mComplete\e[0m\n" + elif [[ "$OSTYPE" == "darwin"* ]]; then + printf "Installing Docker on macOS..." + if ! command -v brew > /dev/null; then + printf "Homebrew not found. Please install Homebrew first.\n" + exit 1 + fi + (brew install --cask docker > /dev/null 2> error.log && open /Applications/Docker.app) & + show_spinner $! + printf " \e[32mComplete\e[0m\n" + printf "Please ensure Docker is running from the macOS toolbar.\n" + else + printf "Unsupported Linux distribution.\n" + exit 1 + fi + fi + + if docker --version > /dev/null 2>&1; then + # Created docker group if not exits + if ! group_exists "docker"; then + create_group "docker"; + fi + if add_user_to_group "docker"; then + if [[ $? -ne 0 ]]; then + printf "Failed to create group, docker configuration failed." + exit 1 + fi + fi + status_stage1="\e[32m$green_tick Complete\e[0m" + error_stage1="" + else + status_stage1="\e[31mFailed\e[0m" + error_stage1="\e[31mFailed to install Docker.\e[0m\n" + fi + display_header +} + +# Function to get the public IP address +get_public_ip() { + echo $(curl -s ifconfig.io) +} + +# Function to get region +get_region() { + echo $(curl -s ifconfig.io/country_code) +} + +# Function to test if the IP is directly reachable from the internet +test_ip_reachability() { + local host_ip=$1 + local port=9080 + local max_retries=1 + local retry=0 + local user_retry_choice="" + local spinner_pid + + while [ $retry -le $max_retries ]; do + display_header + printf "\n\e[1mTesting IP reachability from internet...\e[0m" + show_spinner $$ & # Start the spinner + spinner_pid=$! + + # Start a netcat listener in the background + (nc -l $port > /dev/null 2>&1 &) + listener_pid=$! + sleep 2 # Give the listener time to start + + # Try to connect to the listener using netcat + if echo "test" | nc -w 3 $host_ip $port > /dev/null 2>&1; then + kill $listener_pid > /dev/null 2>&1 + kill $spinner_pid # Stop the spinner + printf "\b\b\e[32mComplete\e[0m]\n" + sleep 3 + return 0 + else + if [ $retry -lt $max_retries ]; then + printf "\nThe IP address %s is not reachable from internet. IP reachability test failed.\n" "$host_ip" + printf "Make sure port 9002 and 9080 are open on your firewall and/or host system and try again.\n" + + kill $spinner_pid # Stop the spinner to interact with user + + read -p "Would you like to retry? (y/n): " user_retry_choice + if [ "$user_retry_choice" != "y" ]; then + kill $listener_pid > /dev/null 2>&1 + return 1 + fi + else + printf "\b\b\e[31mFailed\e[0m\n" + printf "\nYou do not have a public IP that is routable and reachable from internet.\n" + kill $listener_pid > /dev/null 2>&1 + kill $spinner_pid # Stop the spinner + return 1 + fi + fi + ((retry++)) + done + + printf "\nFailed to verify IP reachability after multiple attempts. Exiting.\n" + kill $spinner_pid # Stop the spinner + exit 1 +} + + +check_node_status() { + local container_running=0 + local port_9080_listening=0 + local port_9002_listening=0 + + # Check if container 'erebrus' is running + if sudo docker ps -f name=erebrus | grep "erebrus" >/dev/null; then + container_running=1 + fi + + # Check if ports 9080 and 9002 are listening using lsof command + local lsof_output=$(sudo lsof -nP -iTCP -sTCP:LISTEN) + if echo "${lsof_output}" | grep ":9080.*LISTEN" >/dev/null; then + port_9080_listening=1 + fi + if echo "${lsof_output}" | grep ":9002.*LISTEN" >/dev/null; then + port_9002_listening=1 + fi + + # Return 0 if container is running and both ports are listening + if [ "${container_running}" -eq 1 ] && [ "${port_9080_listening}" -eq 1 ] && [ "${port_9002_listening}" -eq 1 ]; then + return 0 # Container is running and ports are listening + else + return 1 # Either container is not running or ports are not listening + fi +} + +check_mnemonic_format() { + local mnemonic="$1" + # Split the mnemonic into an array of words + IFS=' ' read -r -a words <<< "$mnemonic" + + # Define the required number of words in the mnemonic (12, 15, 18, 21, or 24 typically for BIP39) + local required_words=(12 15 18 21 24) + + # Check if the mnemonic has the correct number of words + local num_words=${#words[@]} + if ! [[ " ${required_words[*]} " =~ " $num_words " ]]; then + return 1 + fi + + # Check if each word in the mnemonic is valid + for word in "${words[@]}"; do + if [[ ! "$word" =~ ^[a-zA-Z]+$ ]]; then + return 1 + fi + done + return 0 +} + +print_final_message() { + if check_node_status; then + printf "\e[32mErebrus node installation is finished.\e[0m\n" + printf "Erebrus Node API is accessible at http://${HOST_IP}:9080\n" + printf "Refer \e[4mhttps://github.com/NetSepio/erebrus/blob/main/docs/docs.md\e[0m for API documentation.\n" + printf "\n\e[32mAll stages completed successfully!\e[0m\n\n" + else + printf "\e[31mFailed to run Erebrus node.\e[0m\n" + fi +} + + +# Function to configure Node environment variables +configure_node() { + clear + printf "\n\e[1mConfiguring Node environment variables...\e[0m\n" + status_stage2="\e[34mIn Progress\e[0m" + display_header + + # Prompt for installation directory and validate input + while true; do + read -p "Enter installation directory (default: current directory): " INSTALL_DIR + INSTALL_DIR=${INSTALL_DIR:-$(pwd)} + sudo mkdir -p "$INSTALL_DIR/wireguard" && sudo chown -R $(whoami):$(whoami) "$INSTALL_DIR" + # Check if directory exists + if [ ! -d "$INSTALL_DIR" ]; then + printf "Error: Directory '%s' does not exist.\n" "$INSTALL_DIR" + # Ask user for confirmation for creation with sudo + read -p "Do you want to create the directory (you may be prompted for your password)? (y/N): " CREATE_DIR + # Check user confirmation (case-insensitive) + if [[ $CREATE_DIR =~ ^[Yy]$ ]]; then + # Create directory with sudo, setting user and group to current user + sudo mkdir -p "$INSTALL_DIR/wireguard" && sudo chown -R $(whoami):$(whoami) "$INSTALL_DIR" + if [[ $? -eq 0 ]]; then + printf "Directory '%s' created successfully.\n" "$INSTALL_DIR" + break + else + printf "Error: Failed to create directory. Please check your permissions.\n" + fi + else + printf "Directory creation skipped.\n" + fi + else + # Directory exists, break the loop + break + fi + done + + + + + + DEFAULT_HOST_IP=$(get_public_ip) + DEFAULT_DOMAIN="http://${DEFAULT_HOST_IP}:9080" + + # Prompt for Public IP + printf "\nAutomatically detected public IP: ${DEFAULT_HOST_IP}\n" + read -p "Do you want to use this public IP? (y/n): " use_default_host_ip + if [ "$use_default_host_ip" = "n" ]; then + read -p "Enter your public IP (default: ${DEFAULT_HOST_IP}): " HOST_IP + HOST_IP=${HOST_IP:-$DEFAULT_HOST_IP} + else + HOST_IP=${DEFAULT_HOST_IP} + fi + + + # Prompt for Chain + printf "Select valid chain from list below:\n" + PS3="Select a chain (e.g. 1): " + options=("APT" "SOL" "EVM" "SUI" "SOON") + select CHAIN in "${options[@]}"; do + if [ -n "$CHAIN" ]; then + break + else + echo "Invalid choice. Please select a valid chain." + fi + done + + while true; do + read -p "Enter your wallet mnemonic: " WALLET_MNEMONIC + if check_mnemonic_format "$WALLET_MNEMONIC"; then + break + else + printf "Wrong mnemonic, try agian with correct mnemonic.\n" + fi + done + + # Prompt for chain selection + while true; do + read -p "Enter the chain (options: SOON, other): " chain + if [[ "$chain" == "SOON" || "$chain" == "other" ]]; then + break + else + printf "Invalid chain option. Please enter either 'SOON' or 'other'.\n" + fi + done + + # If 'SOON' is selected, ask for SOON_PRIVATE_KEY + if [[ "$chain" == "SOON" ]]; then + while true; do + read -p "Enter SOON_PRIVATE_KEY: " SOON_PRIVATE_KEY + if [[ -n "$SOON_PRIVATE_KEY" ]]; then + break + else + printf "SOON_PRIVATE_KEY cannot be empty. Please provide a valid key.\n" + fi + done + fi + + # Display and confirm user-provided variables + printf "\n\e[1mUser Provided Configuration:\e[0m\n" + printf "INSTALL DIR=%s\n" "${INSTALL_DIR}" + printf "REGION=$(get_region)\n" + printf "HOST_IP=%s\n" "${HOST_IP}" + printf "DOMAIN=%s\n" "${DEFAULT_DOMAIN}" + printf "CHAIN=%s\n" "${CHAIN}" + printf "MNEMONIC=%s\n" "${WALLET_MNEMONIC}" + printf "SOON_PRIVATE_KEY=%s\n" "${SOON_PRIVATE_KEY}" + + + read -p "Confirm configuration (y/n): " confirm + if [ "${confirm}" != "y" ]; then + printf "Configuration not confirmed. Exiting.\n" + exit 1 + fi + + # Validate and test IP reachability + test_ip_reachability "$HOST_IP" + if [ $? -eq 1 ]; then + status_stage2="\e[31mFailed\e[0m\n" + error_stage2="\e[31mFailed to configure Erebrus node.\e[0m\n" + return 1 + else + # Write environment variables to .env file + sudo tee ${INSTALL_DIR}/.env < /dev/null 2> error.log) & + show_spinner $! + wait $! + + if [ $? -eq 0 ]; then + status_stage3="\e[32m$green_tick Complete\e[0m" + error_stage3="" + else + status_stage3="\e[31mFailed\e[0m" + error_stage3="\e[31mFailed to run Erebrus node. See error.log for details.\e[0m\n" + fi + display_header +} + +####################################### +# Main script execution starts here +status_stage1="\e[33mPending\e[0m" +status_stage2="\e[33mPending\e[0m" +status_stage3="\e[33mPending\e[0m" +green_tick="\u2714" +skipped_symbol="\u26D4" +clear +display_header + +read -p "Do you want to continue with installation? (y/n): " confirm_installation +if [ "${confirm_installation}" != "y" ]; then + printf "Installation canceled.\n" + exit 1 +fi + +if check_node_status; then + status_stage1="\e[33m$skipped_symbol Skipped\e[0m" + status_stage2="\e[33m$skipped_symbol Skipped\e[0m" + status_stage3="\e[33m$skipped_symbol Skipped\e[0m" + display_header + printf "\e[31mErebrus node is already installed and running. Aborting installation.\e[0m\n" + printf "Refer \e[4mhttps://github.com/NetSepio/erebrus/blob/main/docs/docs.md\e[0m for API documentation.\n\n" + exit 0 +fi + +install_dependencies +if [ -n "${error_stage1}" ]; then + printf "%s${error_stage1}" + exit 1 +else + configure_node + if [ -n "${error_stage2}" ]; then + printf "%s${error_stage2}" + exit 1 + else + run_node + if [ -n "${error_stage3}" ]; then + printf "%s${error_stage3}" + exit 1 + else + print_final_message + fi + fi +fi diff --git a/util/pkg/node/node.go b/util/pkg/node/node.go index f8f9804..5727646 100644 --- a/util/pkg/node/node.go +++ b/util/pkg/node/node.go @@ -6,6 +6,7 @@ import ( "os" "unicode" + soon_solana "github.com/NetSepio/erebrus/blockchain/solana/soon" "github.com/NetSepio/erebrus/core" "github.com/NetSepio/erebrus/util/pkg/speedtest" "github.com/sirupsen/logrus" @@ -145,6 +146,21 @@ func CreateNodeStatus(address string, id string, startTimeStamp int64, name stri IpGeoData: ToJSON(IpGeoAddress), } + if os.Getenv("CHAIN_NAME") == "SOON" { + + peaqDid, _, _ := core.GeneratePeaqDID(23) + + soon_solana.SoonNodeCreation(soon_solana.NodeDetails{ + PrivateKey: os.Getenv("SOON_PRIVATE_KEY"), + PeaqDid: peaqDid, + NodeName: name, + IPAddress: ToJSON(GetOSInfo()), + ISPInfo: ToJSON(GetIPInfo()), + Region: core.GlobalIPInfo.Country, + Location: ToJSON(IpGeoAddress), + }) + } + return nodeStatus }