├── README.md ├── config.ml └── env.sh /README.md: -------------------------------------------------------------------------------- 1 | # node_configuration 2 | Settings and scripts for deploying and installing nodes. 3 | 4 | ## Step 1 Prepare environment: 5 | 6 | ```shell 7 | git clone https://github.com/octra-labs/node_configuration.git 8 | cd node_configuration 9 | chmod +x env.sh 10 | ./env.sh 11 | ``` 12 | 13 | ## Step 2 Generate root hash (validators only): 14 | 15 | **⚠️⚠️ Accepting new validators currently is paused. Wait for announcement ⚠️⚠️** 16 | 17 | ```shell 18 | ocamlfind ocamlopt -o config -thread -linkpkg -package yojson,cohttp-lwt-unix,unix,str,lwt_ppx config.ml 19 | ./config 20 | ``` 21 | 22 | ## Step 3 Generate wallet address: 23 | 24 | ```shell 25 | git clone https://github.com/octra-labs/wallet-gen.git 26 | cd wallet-gen 27 | eval $(opam env) 28 | opam install . --deps-only --yes 29 | dune build --profile release 30 | dune exec ./bin/main.exe 31 | ``` 32 | -------------------------------------------------------------------------------- /config.ml: -------------------------------------------------------------------------------- 1 | open Unix 2 | open Yojson.Basic 3 | open Cohttp_lwt_unix 4 | open Lwt 5 | 6 | let red s = "\027[31m" ^ s ^ "\027[0m" 7 | let green s = "\027[32m" ^ s ^ "\027[0m" 8 | let yellow s = "\027[33m" ^ s ^ "\027[0m" 9 | let blue s = "\027[34m" ^ s ^ "\027[0m" 10 | let magenta s = "\027[35m" ^ s ^ "\027[0m" 11 | let cyan s = "\027[36m" ^ s ^ "\027[0m" 12 | (* need to change color in new version *) 13 | let bold s = "\027[1m" ^ s ^ "\027[0m" 14 | let dim s = "\027[2m" ^ s ^ "\027[0m" 15 | let bg_red s = "\027[41m" ^ s ^ "\027[0m" 16 | let bg_green s = "\027[42m" ^ s ^ "\027[0m" 17 | 18 | let green_box s = 19 | let line = String.make (String.length s + 4) '=' in 20 | "\n" ^ bg_green (line ^ "\n| " ^ s ^ " |\n" ^ line) ^ "\n" 21 | 22 | let error_box s = 23 | let line = String.make (String.length s + 4) '=' in 24 | "\n" ^ bg_red (line ^ "\n| " ^ s ^ " |\n" ^ line) ^ "\n" 25 | 26 | let success_box s = 27 | let line = String.make (String.length s + 4) '*' in 28 | "\n" ^ bg_green (line ^ "\n| " ^ s ^ " |\n" ^ line) ^ "\n" 29 | 30 | let print_header s = 31 | print_endline ("\n" ^ bold (cyan "╺━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╸")); 32 | print_endline (bold (cyan (" " ^ s ^ " "))); 33 | print_endline (bold (cyan "╺━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╸") ^ "\n") 34 | 35 | let print_section s = 36 | print_endline (yellow "\n→ " ^ bold s) 37 | 38 | let print_success s = print_endline (green ("✓ " ^ s ^ ".")) 39 | let print_error s = print_endline (error_box ("ERROR: " ^ s ^ ".")) 40 | let print_info s = print_endline (dim (cyan ("ℹ " ^ s ^ "..."))) 41 | let print_warning s = print_endline (yellow ("⚠ " ^ s ^ ".")) 42 | 43 | let get_command_output cmd = 44 | let ic = Unix.open_process_in cmd in 45 | try 46 | let output = input_line ic in 47 | close_in ic; 48 | if String.trim output = "" then "N/A" else output 49 | with End_of_file -> ( 50 | close_in ic; 51 | "N/A" 52 | ) 53 | 54 | let rec prompt_input prompt validate error_message = 55 | print_string (cyan "→ " ^ bold prompt ^ ": "); 56 | let input = read_line () in 57 | if validate input then ( 58 | print_success "Input successfully validated"; 59 | print_endline ""; 60 | input 61 | ) else ( 62 | print_error error_message; 63 | prompt_input prompt validate error_message 64 | ) 65 | 66 | let non_empty s = String.length (String.trim s) > 0 67 | let is_email s = Str.string_match (Str.regexp "^[^@]+@[^@]+\\.[^@]+$") s 0 68 | let is_number s = 69 | try let n = int_of_string (String.trim s) in n >= 0 with _ -> false 70 | let is_uptime s = 71 | try let n = int_of_string (String.trim s) in n > 0 && n <= 24 with _ -> false 72 | let is_ip_type s = s = "static" || s = "non-static" 73 | let is_yn s = s = "y" || s = "n" 74 | let is_location s = Str.string_match (Str.regexp "^[A-Za-z]+, [A-Za-z]+$") s 0 75 | 76 | let os_type = 77 | print_info "Detecting operating system"; 78 | let uname = get_command_output "uname -s" in 79 | let os = if String.trim uname = "Darwin" then "macOS" else "Linux" in 80 | print_success ("Detected OS: " ^ os); 81 | os 82 | 83 | let () = print_header "Octra node configurator";; 84 | 85 | let () = 86 | print_endline (cyan "Upon successful configuration, you will receive a configuration file"); 87 | print_endline (cyan "containing your unique validator hash. This file will be required"); 88 | print_endline (cyan "when launching your validator node."); 89 | print_endline "";; 90 | 91 | let () = print_section "Personal information";; 92 | print_endline "";; 93 | 94 | let validator_name = prompt_input "Validator name (nickname or organization)" non_empty "Name cannot be empty";; 95 | print_endline "";; 96 | 97 | let validator_website = prompt_input "Validator website (optional)" (fun _ -> true) "";; 98 | print_endline "";; 99 | 100 | let contact_email = prompt_input "Contact email" is_email "Invalid email format";; 101 | print_endline "";; 102 | 103 | let () = print_section "Contacts";; 104 | print_endline "";; 105 | 106 | let telegram_account = prompt_input "Telegram account" non_empty "Account cannot be empty";; 107 | print_endline "";; 108 | 109 | let discord_nickname = prompt_input "Discord nickname" non_empty "Nickname cannot be empty";; 110 | print_endline "";; 111 | 112 | let () = print_section "Server configuration";; 113 | print_endline "";; 114 | 115 | let server_location = prompt_input "Server location (City, Country)" is_location "Location must be in format 'City, Country'";; 116 | print_endline "";; 117 | 118 | let cluster = prompt_input "Cluster (will there be multiple servers? y/n)" is_yn "Please enter 'y' or 'n'";; 119 | print_endline "";; 120 | 121 | let uptime = prompt_input "Planned uptime (hours per day)" is_uptime "Enter a valid number (1-24)";; 122 | print_endline "";; 123 | 124 | let ip_type = prompt_input "IP address type (static/non-static)" is_ip_type "Enter 'static' or 'non-static'";; 125 | print_endline "";; 126 | 127 | let disk_size = prompt_input "Available disk space (MB)" is_number "Enter a valid number";; 128 | print_endline "";; 129 | 130 | let () = print_section "System information collection";; 131 | 132 | let get_os_info () = 133 | print_info "Getting OS information"; 134 | let info = get_command_output "uname -a" in 135 | print_success "OS information collected successfully."; 136 | info 137 | 138 | let get_cpu_info () = 139 | print_info "Extracting CPU features and capabilities"; 140 | let cpu_info = [ 141 | ("brand", (if os_type = "macOS" then 142 | get_command_output "sysctl -n machdep.cpu.brand_string" 143 | else 144 | get_command_output "cat /proc/cpuinfo | grep 'model name' | head -n 1 | cut -d ':' -f2")); 145 | ("cores", (if os_type = "macOS" then 146 | get_command_output "sysctl -n hw.ncpu" 147 | else 148 | get_command_output "nproc")); 149 | ("features", (if os_type = "macOS" then 150 | get_command_output "sysctl -a | grep machdep.cpu.features" 151 | else 152 | get_command_output "lscpu | grep Flags | cut -d ':' -f2")); 153 | ("speed_MHz", (try 154 | if os_type = "macOS" then 155 | let speed = get_command_output "sysctl -n hw.cpufrequency" in 156 | string_of_int (int_of_string speed / 1000000) ^ " MHz" 157 | else 158 | get_command_output "lscpu | grep 'MHz' | awk '{print $3}'" 159 | with _ -> "N/A")) 160 | ] in 161 | print_success "CPU information and capabilities collected successfully."; 162 | cpu_info 163 | 164 | let get_memory_info () = 165 | print_info "Collecting memory information"; 166 | let mem_info = [ 167 | ("total_memory", (try 168 | if os_type = "macOS" then 169 | let size = get_command_output "sysctl -n hw.memsize" in 170 | string_of_int (int_of_string size / (1024 * 1024 * 1024)) ^ " GB" 171 | else 172 | let size = get_command_output "cat /proc/meminfo | grep MemTotal | awk '{print $2}'" in 173 | string_of_int (int_of_string size / 1024) ^ " MB" 174 | with _ -> "N/A")); 175 | ("type", (if os_type = "macOS" then 176 | (* safe call!!! *) 177 | get_command_output "system_profiler SPMemoryDataType | grep 'Type:' | head -n 1 | awk '{print $2}'" 178 | else 179 | get_command_output "dmidecode -t memory | grep Type | head -n 1 | awk '{print $2}'")) 180 | ] in 181 | print_success "Memory information collected successfully."; 182 | mem_info 183 | 184 | let get_ip_address () = 185 | print_info "Getting local IP address"; 186 | let ip = if os_type = "macOS" then 187 | get_command_output "ipconfig getifaddr en0" 188 | else 189 | get_command_output "hostname -I" in 190 | print_success "Local IP collected successfully."; 191 | ip 192 | 193 | let get_external_ip () = 194 | print_info "Getting external IP address"; 195 | let ip = get_command_output "curl -s ifconfig.me" in (* in production use our server for incoming connections, even if it is ipv6 *) 196 | print_success "External IP collected successfully."; 197 | ip 198 | 199 | let os_info = get_os_info () 200 | let cpu_info = get_cpu_info () 201 | let mem_info = get_memory_info () 202 | let ltw_ip = get_ip_address () 203 | let ext_ip = get_external_ip () 204 | 205 | let () = 206 | print_section "Creating configuration"; 207 | print_info "Preparing configuration data"; 208 | let config = `Assoc [ 209 | ("validator", `Assoc [ 210 | ("name", `String validator_name); 211 | ("website", `String validator_website); 212 | ("contact_email", `String contact_email); 213 | ("telegram_account", `String telegram_account); 214 | ("discord_nickname", `String discord_nickname); 215 | ("server_location", `String server_location); 216 | ("cluster", `String cluster); (* in the next version take config 5-31 for a separate cluster and allocate a segment for all machines! *) 217 | ("uptime", `String uptime); 218 | ("ip_type", `String ip_type); 219 | ("disk_size", `String disk_size) 220 | ]); 221 | ("system_info", `Assoc ([ 222 | ("os_type", `String os_type); 223 | ("os", `String os_info); 224 | ("ltw_ip", `String ltw_ip); 225 | ("external_ip", `String ext_ip) 226 | ] @ List.map (fun (k, v) -> (k, `String v)) cpu_info 227 | @ List.map (fun (k, v) -> (k, `String v)) mem_info)) 228 | ] in 229 | 230 | print_info "Registering server in the Octra Network"; 231 | let json_str = Yojson.Basic.pretty_to_string config in 232 | let uri = Uri.of_string "http://161.35.168.80:9000" in (* close the bootstrap gate after all participants have registered!!!!! *) 233 | 234 | try 235 | (* only for a closed group of testers who have been authorized!!! *) 236 | let response = Client.post ~body:(Cohttp_lwt.Body.of_string json_str) uri |> Lwt_main.run in 237 | let body_str = Cohttp_lwt.Body.to_string (snd response) |> Lwt_main.run in 238 | let status = Cohttp.Response.status (fst response) in 239 | 240 | if status = `OK then ( 241 | let res_json = Yojson.Basic.from_string body_str in 242 | let hash_id = res_json |> Util.member "hash" |> Util.to_string in 243 | let filename = hash_id ^ "_config.json" in (* ask lmd about what format will be during reset? *) 244 | let oc = open_out filename in 245 | output_string oc json_str; 246 | close_out oc; 247 | 248 | print_endline ""; 249 | print_endline (success_box "Configuration successfully completed."); 250 | print_endline (green "Configuration details:"); 251 | print_endline (cyan ("• Hash ID: " ^ bold hash_id)); 252 | print_endline (cyan ("• File: " ^ bold filename)); 253 | print_endline (dim "Your configuration has been saved locally and the authorization actor has registered this account in the network"); 254 | print_header "Setup Complete" 255 | ) else ( 256 | print_error ("Server responded with error: " ^ body_str); 257 | print_warning "Please check your configuration and try again"; 258 | print_header "Setup failed" 259 | ) 260 | with e -> 261 | print_error ("Failed to communicate with server: " ^ Printexc.to_string e); 262 | print_warning "Please check your internet connection and try again"; 263 | print_header "Setup failed" 264 | -------------------------------------------------------------------------------- /env.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | OS=$(uname -s) 6 | ARCH=$(uname -m) 7 | BOLD="\033[1m" 8 | RESET="\033[0m" 9 | GREEN="\033[32m" 10 | YELLOW="\033[33m" 11 | RED="\033[31m" 12 | 13 | printf "${BOLD}${YELLOW}Starting environment setup...${RESET}\n" 14 | 15 | if [ "$OS" == "Linux" ]; then 16 | printf "${GREEN}Detected OS: Linux${RESET}\n" 17 | sudo apt-get update 18 | sudo apt-get install -y curl wget git m4 build-essential pkg-config libffi-dev libgmp-dev libssl-dev zlib1g-dev 19 | elif [ "$OS" == "Darwin" ]; then 20 | printf "${GREEN}Detected OS: macOS${RESET}\n" 21 | brew update 22 | brew install curl wget git gmp pkg-config openssl 23 | else 24 | printf "${RED}Unsupported OS: $OS${RESET}\n" 25 | exit 1 26 | fi 27 | 28 | if ! command -v opam &> /dev/null; then 29 | printf "${YELLOW}OPAM not found. Installing...${RESET}\n" 30 | case "$OS" in 31 | Linux) 32 | sudo apt-get install -y opam 33 | ;; 34 | Darwin) 35 | brew install opam 36 | ;; 37 | *) 38 | printf "${RED}Unsupported OS: $OS${RESET}\n" 39 | exit 1 40 | ;; 41 | esac 42 | else 43 | printf "${GREEN}OPAM is already installed.${RESET}\n" 44 | fi 45 | 46 | printf "${YELLOW}Initializing OPAM...${RESET}\n" 47 | opam init --disable-sandboxing -y 48 | opam switch create 5.2.1 -y || true 49 | opam switch 5.2.1 50 | eval $(opam env) 51 | 52 | printf "${YELLOW}Installing OCaml dependencies...${RESET}\n" 53 | opam install -y dune yojson lwt cohttp-lwt-unix ppx_deriving ocamlfind sqlite3 digestif base-threads 54 | opam install -y mirage mirage-crypto mirage-runtime mirage-clock-unix 55 | opam install -y irmin irmin-mirage irmin-http irmin-unix 56 | opam install -y conduit-lwt tls logs fmt 57 | opam install -y alcotest ounit bisect_ppx merlin ocp-indent utop 58 | opam install -y ocamlfind lwt_ppx 59 | opam reinstall -y yojson 60 | 61 | printf "${YELLOW}Verifying installation...${RESET}\n" 62 | ocaml --version 63 | opam --version 64 | dune --version 65 | 66 | printf "${GREEN}Environment setup complete.${RESET}\n" 67 | --------------------------------------------------------------------------------