diff --git a/installer/jshelper/index.js b/installer/jshelper/index.js index f85dcf9..cb08069 100644 --- a/installer/jshelper/index.js +++ b/installer/jshelper/index.js @@ -37,87 +37,64 @@ const funcs = { }, 'validate-account': async (args) => { - checkParams(args, 3); - const rippledUrl = args[0]; - const governorAddress = args[1]; - const accountAddress = args[2]; - const validateFor = args[3] || "register"; + let result = { success: false, result: 'Unknown error.' }; + let hostClient, xrplApi; + try { + checkParams(args, 3); + const rippledUrl = args[0]; + const governorAddress = args[1]; + const accountAddress = args[2]; - await evernode.Defaults.useNetwork(NETWORK); + await evernode.Defaults.useNetwork(NETWORK); - evernode.Defaults.set({ - rippledServer: rippledUrl, - governorAddress: governorAddress - }); + evernode.Defaults.set({ + rippledServer: rippledUrl, + governorAddress: governorAddress + }); - const xrplApi = new evernode.XrplApi(null, { autoReconnect: false }); - await xrplApi.connect(); + xrplApi = new evernode.XrplApi(null, { autoReconnect: false }); + await xrplApi.connect(); - evernode.Defaults.set({ - xrplApi: xrplApi - }); + evernode.Defaults.set({ + xrplApi: xrplApi + }); - const hostClient = new evernode.HostClient(accountAddress, null); + hostClient = new evernode.HostClient(accountAddress, null); - if (!await hostClient.xrplAcc.exists()) - return { success: false, result: "Account not found." }; + if (!await hostClient.xrplAcc.exists()) + result = { success: false, result: "Account not found." }; - await hostClient.connect(); + await hostClient.connect(); - if (validateFor === "register" || validateFor === "re-register") { - // Check whether is there any missed NFT sell offers - try { - const registryAcc = new evernode.XrplAccount(hostClient.config.registryAddress, null); - const regUriToken = await hostClient.getRegistrationUriToken(); + // Check whether host has a registration token. + const regUriToken = await hostClient.getRegistrationUriToken(); + if (regUriToken) + result = { success: true, result: "HAS_REG_TOKEN" }; - if (!regUriToken) { - const regInfo = await hostClient.getHostInfo(accountAddress); + const sellOffer = (await registryAcc.getURITokens()).find(o => o.Issuer == registryAcc.address && o.index == regInfo.uriTokenId && o.Amount); + if (sellOffer) + result = { success: true, result: "HAS_SELL_OFFER" }; - if (regInfo) { - const sellOffer = (await registryAcc.getURITokens()).find(o => o.index == regInfo.uriTokenId && o.Amount); - - if (sellOffer) { - await hostClient.disconnect(); - await xrplApi.disconnect(); - return { success: true }; - } - } - } - - } catch (e) { - await hostClient.disconnect(); - await xrplApi.disconnect(); - return { success: false, result: 'Error occurred in missed sell offers check.' }; - } - } - - const registered = await hostClient.isRegistered(); - // For register validation the host should not be registered in evernode. - // For other validations host should be registered in evernode. - if (validateFor === "register") { + const registered = await hostClient.isRegistered(); if (registered) - return { success: false, result: "Host is already registered." }; - } - else if (!registered) - return { success: false, result: "Host is not registered." }; + result = { success: true, result: "REGISTERED" }; - // Check whether pending transfer exists. - const isTransferPending = await hostClient.isTransferee(); - - // For register validation check the available balance enough for transfer and non transfer registrations. - // For other validations there should not be a pending transfer for the host. - if (validateFor === "register") { - const minEverBalance = isTransferPending ? 1 : hostClient.config.hostRegFee; + // Check whether pending transfer exists. + const transferPending = await hostClient.isTransferee(); + const minEverBalance = transferPending ? 1 : hostClient.config.hostRegFee; const currentBalance = await hostClient.getEVRBalance(); if (currentBalance < minEverBalance) - return { success: false, result: `The account needs minimum balance of ${minEverBalance} EVR. Current balance is ${currentBalance} EVR.` } + result = { success: false, result: `The account needs minimum balance of ${minEverBalance} EVR. Current balance is ${currentBalance} EVR.` } + } catch (e) { + result = { success: false, result: e }; + } finally { + if (hostClient) + await hostClient.disconnect(); + if (xrplApi) + await xrplApi.disconnect(); } - else if (isTransferPending) - return { success: false, result: "There's a pending transfer for this host." }; - await hostClient.disconnect(); - await xrplApi.disconnect(); - return { success: true }; + return result; }, 'validate-keys': async (args) => { diff --git a/installer/setup-test.sh b/installer/setup-test.sh new file mode 100755 index 0000000..0ab4fda --- /dev/null +++ b/installer/setup-test.sh @@ -0,0 +1,112 @@ +#!/bin/bash +# Evernode host setup tool to manage Sashimono installation and host registration. +# This script is also used as the 'evernode' cli alias after the installation. +# usage: ./setup.sh install + +# surrounding braces are needed make the whole script to be buffered on client before execution. +{ + instance_count=10 + mb_error="Evernode Xahau message board exiting with error." + choice_result="" + + function confirm() { + local prompt=$1 + local defaultChoice=${2:-y} #Default choice is set to 'y' if $2 parameter is not provided. + + local choiceDisplay="[Y/n]" + if [ "$defaultChoice" == "n" ]; then + choiceDisplay="[y/N]" + fi + + echo -en "$prompt $choiceDisplay " + local yn="" + read yn /dev/null; then - echo "jq command not found. Installing.." - apt-get install -y jq >/dev/null + if [ "$mode" == "install" ] || [ "$mode" == "uninstall" ] || [ "$mode" == "update" ] || [ "$mode" == "log" ] || [ "$mode" == "transfer" ]; then + [ -n "$2" ] && [ "$2" != "-q" ] && [ "$2" != "-i" ] && echo "Second arg must be -q (Quiet) or -i (Interactive)" && exit 1 + [ "$2" == "-q" ] && interactive=false || interactive=true + [ "$mode" == "transfer" ] && transfer=true || transfer=false + [ "$mode" == "regkey" ] && regkey=true || regkey=false + (! $transfer || $installed || $regkey) && [ "$EUID" -ne 0 ] && echo "Please run with root privileges (sudo)." && exit 1 fi - if ! command -v node &>/dev/null; then - echo "Installing nodejs..." - ! install_nodejs_utility >/dev/null && exit 1 - else - version=$(node -v | cut -d '.' -f1) - version=${version:1} - if [[ $version -lt 20 ]]; then - echo "$evernode requires NodeJs 20.x or later. You system has NodeJs $version installed. Either remove the NodeJs installation or upgrade to NodeJs 20.x." + # Change the relevant setup helper path based on Evernode installation condition and the command mode. + if $installed && [ "$mode" != "update" ]; then + setup_helper_dir="$SASHIMONO_BIN/evernode-setup-helpers" + jshelper_bin="$setup_helper_dir/jshelper/index.js" + fi + + # Format the given KB number into GB units. + function GB() { + echo "$(bc <<<"scale=2; $1 / 1000000") GB" + } + + function install_nodejs_utility() { + apt-get update + apt-get install -y ca-certificates curl gnupg + mkdir -p /etc/apt/keyrings + curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg + + NODE_MAJOR=20 + echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list + apt-get update + apt-get -y install nodejs + } + + function check_common_prereq() { + # Check jq command is installed. + if ! command -v jq &>/dev/null; then + echo "jq command not found. Installing.." + apt-get install -y jq >/dev/null + fi + + if ! command -v node &>/dev/null; then + echo "Installing nodejs..." + ! install_nodejs_utility >/dev/null && exit 1 + else + version=$(node -v | cut -d '.' -f1) + version=${version:1} + if [[ $version -lt 20 ]]; then + echo "$evernode requires NodeJs 20.x or later. You system has NodeJs $version installed. Either remove the NodeJs installation or upgrade to NodeJs 20.x." + exit 1 + fi + fi + } + + function check_prereq() { + echomult "\nChecking initial level prerequisites..." + + check_common_prereq + + # Check bc command is installed. + if ! command -v bc &>/dev/null; then + echo "bc command not found. Installing.." + apt-get -y install bc >/dev/null + fi + + # Check host command is installed. + if ! command -v host &>/dev/null; then + echo "host command not found. Installing.." + apt-get -y install bind9-host >/dev/null + fi + + # Check qrencode command is installed. + if ! command -v qrencode &>/dev/null; then + echo "qrencode command not found. Installing.." + apt-get install -y qrencode >/dev/null + fi + } + + function check_sys_req() { + + # Assign sys resource info to global vars since these will also be used for instance allocation later. + ramKB=$(free | grep Mem | awk '{print $2}') + swapKB=$(free | grep -i Swap | awk '{print $2}') + diskKB=$(df | grep -w /home | head -1 | awk '{print $4}') + [ -z "$diskKB" ] && diskKB=$(df | grep -w / | head -1 | awk '{print $4}') + + # Skip system requirement check in non-production environments if SKIP_SYSREQ=1. + ([ "$NETWORK" != "mainnet" ] && [ "$SKIP_SYSREQ" == "1" ]) && echo "System requirements check skipped." && return 0 + + local proc1=$(ps --no-headers -o comm 1) + if [ "$proc1" != "systemd" ]; then + echo "$evernode host installation requires systemd. Your system does not have systemd running. Aborting." exit 1 fi - fi -} -function check_prereq() { - echomult "\nChecking initial level prerequisites..." + local os=$(grep -ioP '^ID=\K.+' /etc/os-release) + local osversion=$(grep -ioP '^VERSION_ID=\K.+' /etc/os-release) - check_common_prereq + local errors="" + ([ "$os" != "ubuntu" ] || [ "$osversion" != '"20.04"' ]) && errors=" OS: $os $osversion (required: Ubuntu 20.04)\n" + [ $ramKB -lt 2000000 ] && errors="$errors RAM: $(GB $ramKB) (required: 2 GB RAM)\n" + [ $swapKB -lt 2000000 ] && errors="$errors Swap: $(GB $swapKB) (required: 2 GB Swap)\n" + [ $diskKB -lt 4000000 ] && errors="$errors Disk space (/home): $(GB $diskKB) (required: 4 GB)\n" - # Check bc command is installed. - if ! command -v bc &>/dev/null; then - echo "bc command not found. Installing.." - apt-get -y install bc >/dev/null - fi - - # Check host command is installed. - if ! command -v host &> /dev/null; then - echo "host command not found. Installing.." - apt-get -y install bind9-host >/dev/null - fi - - # Check qrencode command is installed. - if ! command -v qrencode &>/dev/null; then - echo "qrencode command not found. Installing.." - apt-get install -y qrencode >/dev/null - fi -} - -function check_sys_req() { - - # Assign sys resource info to global vars since these will also be used for instance allocation later. - ramKB=$(free | grep Mem | awk '{print $2}') - swapKB=$(free | grep -i Swap | awk '{print $2}') - diskKB=$(df | grep -w /home | head -1 | awk '{print $4}') - [ -z "$diskKB" ] && diskKB=$(df | grep -w / | head -1 | awk '{print $4}') - - # Skip system requirement check in non-production environments if SKIP_SYSREQ=1. - ([ "$NETWORK" != "mainnet" ] && [ "$SKIP_SYSREQ" == "1" ]) && echo "System requirements check skipped." && return 0 - - - local proc1=$(ps --no-headers -o comm 1) - if [ "$proc1" != "systemd" ]; then - echo "$evernode host installation requires systemd. Your system does not have systemd running. Aborting." - exit 1 - fi - - local os=$(grep -ioP '^ID=\K.+' /etc/os-release) - local osversion=$(grep -ioP '^VERSION_ID=\K.+' /etc/os-release) - - local errors="" - ([ "$os" != "ubuntu" ] || [ "$osversion" != '"20.04"' ]) && errors=" OS: $os $osversion (required: Ubuntu 20.04)\n" - [ $ramKB -lt 2000000 ] && errors="$errors RAM: $(GB $ramKB) (required: 2 GB RAM)\n" - [ $swapKB -lt 2000000 ] && errors="$errors Swap: $(GB $swapKB) (required: 2 GB Swap)\n" - [ $diskKB -lt 4000000 ] && errors="$errors Disk space (/home): $(GB $diskKB) (required: 4 GB)\n" - - if [ -z "$errors" ]; then - echo "System check complete. Your system is capable of becoming an $evernode host." - else - echomult "Your system does not meet following $evernode system requirements:\n $errors" - echomult "$evernode host registration requires Ubuntu 20.04 with minimum 2 GB RAM, + if [ -z "$errors" ]; then + echo "System check complete. Your system is capable of becoming an $evernode host." + else + echomult "Your system does not meet following $evernode system requirements:\n $errors" + echomult "$evernode host registration requires Ubuntu 20.04 with minimum 2 GB RAM, 2 GB Swap and 4 GB free disk space for /home. Aborting setup." - exit 1 - fi -} + exit 1 + fi + } -function set_environment_configs() { + function set_environment_configs() { - sudo -u $noroot_user mkdir -p $setup_helper_dir - echomult "\nDownloading Environment configuration...\n" - sudo -u $noroot_user curl $config_url --output $config_json_path + sudo -u $noroot_user mkdir -p $setup_helper_dir + echomult "\nDownloading Environment configuration...\n" + sudo -u $noroot_user curl $config_url --output $config_json_path - # Network config selection. + # Network config selection. - echomult "\nChecking Evernode $NETWORK environment details..." + echomult "\nChecking Evernode $NETWORK environment details..." - if ! jq -e ".${NETWORK}" "$config_json_path" >/dev/null 2>&1; then - echomult "Sorry the specified environment has not been configured yet..\n" && exit 1 - fi - - export EVERNODE_GOVERNOR_ADDRESS=${OVERRIDE_EVERNODE_GOVERNOR_ADDRESS:-$(jq -r ".$NETWORK.governorAddress" $config_json_path)} - default_rippled_server=$(jq -r ".$NETWORK.rippledServer" $config_json_path) - -} - -function init_setup_helpers() { - - echo "Downloading setup support files..." - - local jshelper_dir=$(dirname $jshelper_bin) - rm -r $jshelper_dir >/dev/null 2>&1 - sudo -u $noroot_user mkdir -p $jshelper_dir - - - if [ ! -f "$jshelper_bin" ]; then - pushd $jshelper_dir >/dev/null 2>&1 - sudo -u $noroot_user curl -L $jshelper_url --output jshelper.tar.gz - sudo -u $noroot_user tar zxf jshelper.tar.gz --strip-components=1 - rm jshelper.tar.gz - popd >/dev/null 2>&1 - fi - [ ! -f "$jshelper_bin" ] && echo "Could not download helper tool for setup checks." && exit 1 - echo -e "Done.\n" -} - -function exec_jshelper() { - - # Create fifo file to read response data from the helper script. - local resp_file=$setup_helper_dir/helper_fifo - [ -p $resp_file ] || sudo -u $noroot_user mkfifo $resp_file - - # Execute js helper asynchronously while collecting response to fifo file. - sudo -u $noroot_user RESPFILE=$resp_file $nodejs_util_bin $jshelper_bin "$@" "network:$NETWORK" >/dev/null 2>&1 & - local pid=$! - local result=$(cat $resp_file) && [ "$result" != "-" ] && echo $result - - # Wait for js helper to exit and reflect the error exit code in this function return. - wait $pid && [ $? -eq 0 ] && rm $resp_file && return 0 - rm $resp_file && return 1 -} - -function exec_jshelper_root() { - - # Create fifo file to read response data from the helper script. - local resp_file=$setup_helper_dir/helper_fifo - [ -p $resp_file ] || mkfifo $resp_file - - # Execute js helper asynchronously while collecting response to fifo file. - RESPFILE=$resp_file $nodejs_util_bin $jshelper_bin "$@" "network:$NETWORK" >/dev/null 2>&1 & - local pid=$! - local result=$(cat $resp_file) && [ "$result" != "-" ] && echo $result - - # Wait for js helper to exit and reflect the error exit code in this function return. - wait $pid && [ $? -eq 0 ] && rm $resp_file && return 0 - rm $resp_file && return 1 -} - -function resolve_filepath() { - # name reference the variable name provided as first argument. - local -n filepath=$1 - local option=$2 - local prompt="${*:3} " - - while [ -z "$filepath" ]; do - read -p "$prompt" filepath /dev/null 2>&1 ; then - local port="80" - echo "Verifying domain $inetaddr on port $port..." - local domain_result=$(exec_jshelper_root validate-domain $inetaddr $port) - [[ "$domain_result" == "ok" ]] && echo "Domain verification successful." && return 0 - - if [ "$domain_result" == "listen_error" ]; then - echomult "Could not initiate domain verification. It's likely that port $port is already in use by another application.\n - It's recommended that you abandon the setup and correct this. You should consider continuing only if you are an advanced user - who knows what they are doing, and is going to provide your own SSL certificates." - confirm "Do you want to abandon the setup (recommended)?" && echo "Setup abandoned." && exit 1 - echo "Continuing with unverified domain $inetaddr" && return 0 + if ! jq -e ".${NETWORK}" "$config_json_path" >/dev/null 2>&1; then + echomult "Sorry the specified environment has not been configured yet..\n" && exit 1 fi - [[ "$domain_result" == "domain_error" ]] && - echo "Domain verification for $inetaddr failed. Please make sure that this host is reachable via $inetaddr" - fi + export EVERNODE_GOVERNOR_ADDRESS=${OVERRIDE_EVERNODE_GOVERNOR_ADDRESS:-$(jq -r ".$NETWORK.governorAddress" $config_json_path)} + default_rippled_server=$(jq -r ".$NETWORK.rippledServer" $config_json_path) - # Reaching this point means some error has occured. So we clear the inetaddress to allow to try again. - inetaddr="" && return 1 -} + } -function validate_inet_addr() { - # inert address cannot be empty and cannot contain spaces. - [ -z "$inetaddr" ] || [[ $inetaddr = *" "* ]] && inetaddr="" && return 1 + function init_setup_helpers() { - # Attempt to resolve ip (in case inetaddr is a DNS address) - # This will resolve correctly if inetaddr is a valid ip or dns address. + echo "Downloading setup support files..." - local resolved_ips=$(getent hosts $inetaddr | wc -l) + local jshelper_dir=$(dirname $jshelper_bin) + rm -r $jshelper_dir >/dev/null 2>&1 + sudo -u $noroot_user mkdir -p $jshelper_dir - # Check if there is more than one IP address - if [ $resolved_ips -eq 1 ]; then - return 0 - elif [ $resolved_ips -gt 1 ]; then - echo "Your domain ($inetaddr) must point to a single IP address." - fi + if [ ! -f "$jshelper_bin" ]; then + pushd $jshelper_dir >/dev/null 2>&1 + sudo -u $noroot_user curl -L $jshelper_url --output jshelper.tar.gz + sudo -u $noroot_user tar zxf jshelper.tar.gz --strip-components=1 + rm jshelper.tar.gz + popd >/dev/null 2>&1 + fi + [ ! -f "$jshelper_bin" ] && echo "Could not download helper tool for setup checks." && exit 1 + echo -e "Done.\n" + } - # If invalid, reset inetaddr and return with non-zero code. - inetaddr="" && return 1 + function exec_jshelper() { -} + # Create fifo file to read response data from the helper script. + local resp_file=$setup_helper_dir/helper_fifo + [ -p $resp_file ] || sudo -u $noroot_user mkfifo $resp_file -function validate_positive_decimal() { - ! [[ $1 =~ ^(0*[1-9][0-9]*(\.[0-9]+)?|0+\.[0-9]*[1-9][0-9]*)$ ]] && return 1 - return 0 -} + # Execute js helper asynchronously while collecting response to fifo file. + sudo -u $noroot_user RESPFILE=$resp_file $nodejs_util_bin $jshelper_bin "$@" "network:$NETWORK" >/dev/null 2>&1 & + local pid=$! + local result=$(cat $resp_file) && [ "$result" != "-" ] && echo $result -function validate_rippled_url() { - ! [[ $1 =~ ^(wss?:\/\/)([^\/|^:|^ ]{3,})(:([0-9]{1,5}))?$ ]] && echo "Rippled URL must be a valid URL that starts with 'wss://'" && return 1 + # Wait for js helper to exit and reflect the error exit code in this function return. + wait $pid && [ $? -eq 0 ] && rm $resp_file && return 0 + rm $resp_file && return 1 + } - echo "Checking server $1..." - ! exec_jshelper validate-server $1 && echo "Could not communicate with the rippled server." && return 1 - return 0 -} + function exec_jshelper_root() { -function validate_email_address() { - local emailAddress=$1 - email_address_length=${#emailAddress} - ( ( ! [[ "$email_address_length" -le 40 ]] && echo "Email address length should not exceed 40 characters." ) || - ( ! [[ $emailAddress =~ .+@.+ ]] && echo "Email address is invalid." ) ) || return 0 - return 1 -} + # Create fifo file to read response data from the helper script. + local resp_file=$setup_helper_dir/helper_fifo + [ -p $resp_file ] || mkfifo $resp_file -function set_inet_addr() { + # Execute js helper asynchronously while collecting response to fifo file. + RESPFILE=$resp_file $nodejs_util_bin $jshelper_bin "$@" "network:$NETWORK" >/dev/null 2>&1 & + local pid=$! + local result=$(cat $resp_file) && [ "$result" != "-" ] && echo $result - # Skip system requirement check in non-production environments if $NO_DOMAIN=1. - if [ "$NETWORK" == "mainnet" ] || [[ "$NETWORK" != "mainnet" && "$NO_DOMAIN" == "" ]] ; then - echo "" - while [ -z "$inetaddr" ]; do - read -ep "Please specify the domain name that this host is reachable at: " inetaddr /dev/null 2>&1; then + local port="80" + echo "Verifying domain $inetaddr on port $port..." + local domain_result=$(exec_jshelper_root validate-domain $inetaddr $port) + [[ "$domain_result" == "ok" ]] && echo "Domain verification successful." && return 0 -# Validate country code and convert to uppercase if valid. -function resolve_countrycode() { - # If invalid, reset countrycode and return with non-zero code. - if ! [[ $countrycode =~ ^[A-Za-z][A-Za-z]$ ]] ; then - countrycode="" + if [ "$domain_result" == "listen_error" ]; then + echomult "Could not initiate domain verification. It's likely that port $port is already in use by another application.\n + It's recommended that you abandon the setup and correct this. You should consider continuing only if you are an advanced user + who knows what they are doing, and is going to provide your own SSL certificates." + confirm "Do you want to abandon the setup (recommended)?" && echo "Setup abandoned." && exit 1 + echo "Continuing with unverified domain $inetaddr" && return 0 + fi + + [[ "$domain_result" == "domain_error" ]] && + echo "Domain verification for $inetaddr failed. Please make sure that this host is reachable via $inetaddr" + fi + + # Reaching this point means some error has occured. So we clear the inetaddress to allow to try again. + inetaddr="" && return 1 + } + + function validate_inet_addr() { + # inert address cannot be empty and cannot contain spaces. + [ -z "$inetaddr" ] || [[ $inetaddr = *" "* ]] && inetaddr="" && return 1 + + # Attempt to resolve ip (in case inetaddr is a DNS address) + # This will resolve correctly if inetaddr is a valid ip or dns address. + + local resolved_ips=$(getent hosts $inetaddr | wc -l) + + # Check if there is more than one IP address + if [ $resolved_ips -eq 1 ]; then + return 0 + elif [ $resolved_ips -gt 1 ]; then + echo "Your domain ($inetaddr) must point to a single IP address." + fi + + # If invalid, reset inetaddr and return with non-zero code. + inetaddr="" && return 1 + + } + + function validate_positive_decimal() { + ! [[ $1 =~ ^(0*[1-9][0-9]*(\.[0-9]+)?|0+\.[0-9]*[1-9][0-9]*)$ ]] && return 1 + return 0 + } + + function validate_rippled_url() { + ! [[ $1 =~ ^(wss?:\/\/)([^\/|^:|^ ]{3,})(:([0-9]{1,5}))?$ ]] && echo "Rippled URL must be a valid URL that starts with 'wss://'" && return 1 + + echo "Checking server $1..." + ! exec_jshelper validate-server $1 && echo "Could not communicate with the rippled server." && return 1 + return 0 + } + + function validate_email_address() { + local emailAddress=$1 + email_address_length=${#emailAddress} + ( (! [[ "$email_address_length" -le 40 ]] && echo "Email address length should not exceed 40 characters.") || + (! [[ $emailAddress =~ .+@.+ ]] && echo "Email address is invalid.")) || return 0 return 1 - else - countrycode=$(echo $countrycode | tr 'a-z' 'A-Z') - return 0 - fi -} + } -function set_country_code() { + function set_inet_addr() { - # Attempt to auto-detect in interactive mode or if 'auto' is specified. - echo "Checking country code..." - echo "Using GeoLite2 data created by MaxMind, available from https://www.maxmind.com" + # Skip system requirement check in non-production environments if $NO_DOMAIN=1. + if [ "$NETWORK" == "mainnet" ] || [[ "$NETWORK" != "mainnet" && "$NO_DOMAIN" == "" ]]; then + echo "" + while [ -z "$inetaddr" ]; do + read -ep "Please specify the domain name that this host is reachable at: " inetaddr Usage: generate_qrcode " - return 1 - fi - local input_string="$1" - qrencode -s 1 -l L -t UTF8 "$input_string" -} + echo -e "Affordable extra transaction fee is set as $fee XAH Drops.\n" -function generate_and_save_keyfile() { + else + extra_txn_fee=$fee + fi + } - account_json=$(exec_jshelper generate-account) || { echo "Error occurred in account setting up."; exit 1; } - xrpl_address=$(jq -r '.address' <<< "$account_json") - xrpl_secret=$(jq -r '.secret' <<< "$account_json") + function set_email_address() { - if [ "$#" -ne 1 ]; then - echomult "Error: Please provide the full path of the secret file." - return 1 - fi + local emailAddress="" + while true; do + read -ep "Specify the contact email address for your host (this will be published on the host registry and is publicly visible to anyone): " emailAddress /dev/null) - if [ "$existing_secret" != "null" ] && [ "$existing_secret" != "-" ]; then - account_json=$(exec_jshelper generate-account $existing_secret) || { echomult "Error occurred when existing account retrieval."; exit 1; } - xrpl_address=$(jq -r '.address' <<< "$account_json") - xrpl_secret=$(jq -r '.secret' <<< "$account_json") + function set_auto_update() { + enable_auto_update=false + if $interactive; then + if confirm "\nDo you want to subscribe for auto-updates?\nNOTE: The auto-update service is offered subject to the terms set out in the Evernode Software Licence." "n"; then + enable_auto_update=true + fi + fi + } - chmod 400 "$key_file_path" && \ - chown $MB_XRPL_USER: $key_file_path || { echomult "Error occurred in permission and ownership assignment of key file."; exit 1; } - echomult "Retrived account details via secret.\n" - return 0 + function set_regular_key() { + [ "$EUID" -ne 0 ] && echo "Please run with root privileges (sudo)." && exit 1 + + ! sudo -u $MB_XRPL_USER MB_DATA_DIR=$MB_XRPL_DATA node $MB_XRPL_BIN regkey $1 && + echo "There was an error in changing the regular key." && return 1 + } + + function set_transferee_address() { + # Here we set the default transferee address as 'CURRENT_HOST_ADDRESS', but we set it to the exact current host address in host client side. + [ -z $transferee_address ] && transferee_address='' + + if $interactive; then + confirm "\nDo you want to set the current host account as the transferee's account?" && return 0 + + local address='' + while true; do + read -ep "Specify the Xahau account address of the transferee: " address Usage: generate_qrcode " + return 1 + fi + local input_string="$1" + qrencode -s 1 -l L -t UTF8 "$input_string" + } + + function generate_and_save_keyfile() { + + account_json=$(exec_jshelper generate-account) || { + echo "Error occurred in account setting up." + exit 1 + } + xrpl_address=$(jq -r '.address' <<<"$account_json") + xrpl_secret=$(jq -r '.secret' <<<"$account_json") + + if [ "$#" -ne 1 ]; then + echomult "Error: Please provide the full path of the secret file." + return 1 + fi + + key_file_path="$1" + + key_dir=$(dirname "$key_file_path") + if [ ! -d "$key_dir" ]; then + mkdir -p "$key_dir" + fi + + if [ "$key_file_path" == "$default_key_filepath" ]; then + parent_directory=$(dirname "$key_file_path") + chmod -R 500 "$parent_directory" && + chown -R $MB_XRPL_USER: "$parent_directory" || { + echomult "Error occurred in permission and ownership assignment of key file directory." + exit 1 + } + fi + + if [ -e "$key_file_path" ]; then + if confirm "The file '$key_file_path' already exists. Do you want to continue using that key file?\nPressing 'n' would terminate the installation."; then + echomult "Continuing with the existing key file." + existing_secret=$(jq -r '.xrpl.secret' "$key_file_path" 2>/dev/null) + if [ "$existing_secret" != "null" ] && [ "$existing_secret" != "-" ]; then + account_json=$(exec_jshelper generate-account $existing_secret) || { + echomult "Error occurred when existing account retrieval." + exit 1 + } + xrpl_address=$(jq -r '.address' <<<"$account_json") + xrpl_secret=$(jq -r '.secret' <<<"$account_json") + + chmod 400 "$key_file_path" && + chown $MB_XRPL_USER: $key_file_path || { + echomult "Error occurred in permission and ownership assignment of key file." + exit 1 + } + echomult "Retrived account details via secret.\n" + return 0 + else + echomult "Error: Existing secret file does not have the expected format." + exit 1 + fi else - echomult "Error: Existing secret file does not have the expected format." exit 1 fi else - exit 1 + + echo "{ \"xrpl\": { \"secret\": \"$xrpl_secret\" } }" >"$key_file_path" && + chmod 400 "$key_file_path" && + chown $MB_XRPL_USER: $key_file_path && + echomult "Key file saved successfully at $key_file_path" || { + echomult "Error occurred in permission and ownership assignment of key file." + exit 1 + } + + return 0 fi - else - echo "{ \"xrpl\": { \"secret\": \"$xrpl_secret\" } }" > "$key_file_path" && \ - chmod 400 "$key_file_path" && \ - chown $MB_XRPL_USER: $key_file_path && \ - echomult "Key file saved successfully at $key_file_path" || { echomult "Error occurred in permission and ownership assignment of key file."; exit 1; } + exit 1 + } - return 0 - fi + function set_host_xrpl_account() { + local secret_key="" + if [ -f "$MB_XRPL_CONFIG" ]; then + xrpl_address=$(cat "$MB_XRPL_CONFIG" | jq -r '.xrpl.address') + key_file_path=$(cat "$MB_XRPL_CONFIG" | jq -r '.xrpl.secretPath') - exit 1 -} + if [ -f $key_file_path ]; then + secret_key=$(jq -r '.xrpl.secret' "$key_file_path" 2>/dev/null) + fi + fi -function set_host_xrpl_account() { - local account_validate_criteria="register" - local required_balance=0 - [ ! -z $1 ] && account_validate_criteria=$1 + if [ ! -z $secret_key ]; then + local operation="register" + ! confirm "\nAre you performing a fresh Evernode installation? + \nNOTE: Pressing 'n' implies that you are in the process of transferring from a previous installation in $NETWORK." && operation="re-register" - local reg_fee=$(exec_jshelper access-evernode-cfg $rippled_server $EVERNODE_GOVERNOR_ADDRESS hostRegFee) + if [ $operation == "register" ]; then + confirm "\nDo you want to use the default key file path ${default_key_filepath} to save the new account key?" && key_file_path=$default_key_filepath - # Create MB_XRPL_USER as we require that user for secret key ownership management. - if ! grep -q "^$MB_XRPL_USER:" /etc/passwd; then - echomult "Creating Message-board User..." - useradd --shell /usr/sbin/nologin -m $MB_XRPL_USER 2>/dev/null + if [ "$key_file_path" != "$default_key_filepath" ]; then + while true; do + read -ep "Specify the preferred key file path: " key_file_path reserve_base_xrp + reserve_inc_xrp * n + # reserve_inc_xrp * n => trustline reserve + reg_token_reserve + (reserve_inc_xrp * instance_count) + local inc_reserves_count=$((1 + 1 + $alloc_instcount)) + min_reserve_requirement=$(exec_jshelper compute-xah-requirement $rippled_server $inc_reserves_count) || { + echomult "Error occuured in checking XAH requirement." + exit 1 + } - done + min_xah_requirement=$(echo "$min_operational_cost_per_month*$initial_operational_duration + $min_reserve_requirement" | bc) + + generate_and_save_keyfile "$key_file_path" + + local reg_fee=$(exec_jshelper access-evernode-cfg $rippled_server $EVERNODE_GOVERNOR_ADDRESS hostRegFee) + + echomult "Your host account with the address $xrpl_address will be on Xahau $NETWORK. + \nThe secret key of the account is located at $key_file_path. + \nNOTE: It is your responsibility to safeguard/backup this file in a secure manner. + \nIf you lose it, you will not be able to access any funds in your Host account. NO ONE else can recover it. + + \n\nThis is the account that will represent this host on the Evernode host registry. You need to load up the account with following funds in order to continue with the installation. + \n1. At least $min_xah_requirement XAH to cover regular transaction fees for the first three months. + \n2. At least $reg_fee EVR to cover Evernode registration fee. + \n\nYou can scan the following QR code in your wallet app to send funds based on the account condition:\n" + else + while true; do + read -ep "Specify the Xahau account address: " xrpl_address reserve_base_xrp + reserve_inc_xrp * n - # reserve_inc_xrp * n => trustline reserve + reg_token_reserve + (reserve_inc_xrp * instance_count) - local inc_reserves_count=$((1 + 1 + $alloc_instcount)) - min_reserve_requirement=$(exec_jshelper compute-xah-requirement $rippled_server $inc_reserves_count) || { echomult "Error occuured in checking XAH requirement."; exit 1; } - - min_xah_requirement=$(echo "$min_operational_cost_per_month*$initial_operational_duration + $min_reserve_requirement" | bc ) - - generate_and_save_keyfile "$key_file_path" - - echomult "Your host account with the address $xrpl_address will be on Xahau $NETWORK. - \nThe secret key of the account is located at $key_file_path. - \nNOTE: It is your responsibility to safeguard/backup this file in a secure manner. - \nIf you lose it, you will not be able to access any funds in your Host account. NO ONE else can recover it. - - \n\nThis is the account that will represent this host on the Evernode host registry. You need to load up the account with following funds in order to continue with the installation. - \n1. At least $min_xah_requirement XAH to cover regular transaction fees for the first three months. - \n2. At least $reg_fee EVR to cover Evernode registration fee. - \n\nYou can scan the following QR code in your wallet app to send funds based on the account condition:\n" generate_qrcode "$xrpl_address" - account_condition='-' - - echomult "\nChecking the account condition..." - echomult "To set up your host account, ensure a deposit of $min_xah_requirement XAH to cover the regular transaction fees for the first three months." - - required_balance=$min_xah_requirement - while true ; do - wait_call "exec_jshelper check-balance $rippled_server $EVERNODE_GOVERNOR_ADDRESS $xrpl_address NATIVE $required_balance" "Thank you. [OUTPUT] XAH balance is there in your host account." \ - && break - confirm "\nDo you want to re-check the balance?\nPressing 'n' would terminate the installation." || exit 1 - done - - - echomult "\nPreparing host account..." - while true ; do - wait_call "exec_jshelper prepare-host $rippled_server $EVERNODE_GOVERNOR_ADDRESS $xrpl_address $xrpl_secret $inetaddr" "Account preparation is successfull." && break - confirm "\nDo you want to re-try account preparation?\nPressing 'n' would terminate the installation." || exit 1 - done - - echomult "\n\nIn order to register in Evernode you need to have $reg_fee EVR balance in your host account. Please deposit the required registration fee in EVRs. - \nYou can scan the provided QR code in your wallet app to send funds:" - - required_balance=$reg_fee - while true ; do - wait_call "exec_jshelper check-balance $rippled_server $EVERNODE_GOVERNOR_ADDRESS $xrpl_address ISSUED $required_balance" "Thank you. [OUTPUT] EVR balance is there in your host account." \ - && break - confirm "\nDo you want to re-check the balance?\nPressing 'n' would terminate the installation." || exit 1 - done - - elif [ "$account_validate_criteria" == "transfer" ] || [ "$account_validate_criteria" == "re-register" ]; then - - if [ "$account_validate_criteria" == "re-register" ]; then - account_validate_criteria="register" + # Check account registration status + local registration_status="" + if [ $registration_status == "REGISTERED" ]; then + exit 0 + elif [ $registration_status == "HAS_REG_TOKEN" || $registration_status == "HAS_SELL_OFFER" ]; then + exit 0 fi - while true ; do - read -ep "Specify the Xahau account address: " xrpl_address /dev/null - ! [[ $xrpl_secret =~ ^s[1-9A-HJ-NP-Za-km-z]{25,35}$ ]] && echo "Invalid account secret." && continue + fi - echo "Checking account keys..." - ! exec_jshelper validate-keys $rippled_server $xrpl_address $xrpl_secret && xrpl_secret="" && continue + if [ "$account_validate_criteria" == "register" ]; then + account_condition='-' - # Modifying key file ownership to MB_XRPL_USER. - chown $MB_XRPL_USER: $key_file_path && \ - chmod 400 $key_file_path || { echomult "Error occurred in permission and ownership assignment of key file."; exit 1; } + echomult "\nChecking the account condition..." + echomult "To set up your host account, ensure a deposit of $min_xah_requirement XAH to cover the regular transaction fees for the first three months." - xrpl_account_secret=$xrpl_secret + required_balance=$min_xah_requirement + while true; do + wait_call "exec_jshelper check-balance $rippled_server $EVERNODE_GOVERNOR_ADDRESS $xrpl_address NATIVE $required_balance" "Thank you. [OUTPUT] XAH balance is there in your host account." && + break + confirm "\nDo you want to re-check the balance?\nPressing 'n' would terminate the installation." || exit 1 + done - break - done - fi + echomult "\nPreparing host account..." + while true; do + wait_call "exec_jshelper prepare-host $rippled_server $EVERNODE_GOVERNOR_ADDRESS $xrpl_address $xrpl_secret $inetaddr" "Account preparation is successfull." && break + confirm "\nDo you want to re-try account preparation?\nPressing 'n' would terminate the installation." || exit 1 + done - xrpl_account_address=$xrpl_address - xrpl_account_secret_path=$key_file_path -} + echomult "\n\nIn order to register in Evernode you need to have $reg_fee EVR balance in your host account. Please deposit the required registration fee in EVRs. + \nYou can scan the provided QR code in your wallet app to send funds:" -function install_failure() { - echo "There was an error during installation. Please provide the file $logfile to Evernode team. Thank you." - exit 1 -} + required_balance=$reg_fee + while true; do + wait_call "exec_jshelper check-balance $rippled_server $EVERNODE_GOVERNOR_ADDRESS $xrpl_address ISSUED $required_balance" "Thank you. [OUTPUT] EVR balance is there in your host account." && + break + confirm "\nDo you want to re-check the balance?\nPressing 'n' would terminate the installation." || exit 1 + done + fi -function uninstall_failure() { - echo "There was an error during uninstallation." - exit 1 -} + xrpl_account_address=$xrpl_address + xrpl_account_secret_path=$key_file_path + } -function online_version_timestamp() { - latest_version_data=$(curl -s "$latest_version_endpoint") - latest_version_timestamp=$(echo "$latest_version_data" | jq -r '.published_at') - echo "$latest_version_timestamp" -} + function install_failure() { + echo "There was an error during installation. Please provide the file $logfile to Evernode team. Thank you." + exit 1 + } -function enable_evernode_auto_updater() { - [ "$EUID" -ne 0 ] && echo "Please run with root privileges (sudo)." && exit 1 - enable_auto_update=true + function uninstall_failure() { + echo "There was an error during uninstallation." + exit 1 + } - # Create the service. - echo "[Unit] + function online_version_timestamp() { + latest_version_data=$(curl -s "$latest_version_endpoint") + latest_version_timestamp=$(echo "$latest_version_data" | jq -r '.published_at') + echo "$latest_version_timestamp" + } + + function enable_evernode_auto_updater() { + [ "$EUID" -ne 0 ] && echo "Please run with root privileges (sudo)." && exit 1 + enable_auto_update=true + + # Create the service. + echo "[Unit] Description=Service for the Evernode auto-update. After=network.target [Service] @@ -1047,8 +1085,8 @@ ExecStart=/usr/bin/evernode update -q [Install] WantedBy=multi-user.target" >/etc/systemd/system/$EVERNODE_AUTO_UPDATE_SERVICE.service - # Create a timer for the service (every two hours). - echo "[Unit] + # Create a timer for the service (every two hours). + echo "[Unit] Description=Timer for the Evernode auto-update. # Allow manual starts RefuseManualStart=no @@ -1064,794 +1102,781 @@ RandomizedDelaySec=7200 [Install] WantedBy=timers.target" >/etc/systemd/system/$EVERNODE_AUTO_UPDATE_SERVICE.timer - # Reload the systemd daemon. - systemctl daemon-reload + # Reload the systemd daemon. + systemctl daemon-reload - echo "Enabling Evernode auto update service..." - systemctl enable $EVERNODE_AUTO_UPDATE_SERVICE.service + echo "Enabling Evernode auto update service..." + systemctl enable $EVERNODE_AUTO_UPDATE_SERVICE.service - echo "Enabling Evernode auto update timer..." - systemctl enable $EVERNODE_AUTO_UPDATE_SERVICE.timer - echo "Starting Evernode auto update timer..." - systemctl start $EVERNODE_AUTO_UPDATE_SERVICE.timer -} + echo "Enabling Evernode auto update timer..." + systemctl enable $EVERNODE_AUTO_UPDATE_SERVICE.timer + echo "Starting Evernode auto update timer..." + systemctl start $EVERNODE_AUTO_UPDATE_SERVICE.timer + } -function remove_evernode_auto_updater() { - [ "$EUID" -ne 0 ] && echo "Please run with root privileges (sudo)." && exit 1 - enable_auto_update=false + function remove_evernode_auto_updater() { + [ "$EUID" -ne 0 ] && echo "Please run with root privileges (sudo)." && exit 1 + enable_auto_update=false - echo "Removing Evernode auto update timer..." - systemctl stop $EVERNODE_AUTO_UPDATE_SERVICE.timer - systemctl disable $EVERNODE_AUTO_UPDATE_SERVICE.timer - service_path="/etc/systemd/system/$EVERNODE_AUTO_UPDATE_SERVICE.timer" - rm -f $service_path + echo "Removing Evernode auto update timer..." + systemctl stop $EVERNODE_AUTO_UPDATE_SERVICE.timer + systemctl disable $EVERNODE_AUTO_UPDATE_SERVICE.timer + service_path="/etc/systemd/system/$EVERNODE_AUTO_UPDATE_SERVICE.timer" + rm -f $service_path - echo "Removing Evernode auto update service..." - systemctl stop $EVERNODE_AUTO_UPDATE_SERVICE.service - systemctl disable $EVERNODE_AUTO_UPDATE_SERVICE.service - service_path="/etc/systemd/system/$EVERNODE_AUTO_UPDATE_SERVICE.service" - rm -f $service_path + echo "Removing Evernode auto update service..." + systemctl stop $EVERNODE_AUTO_UPDATE_SERVICE.service + systemctl disable $EVERNODE_AUTO_UPDATE_SERVICE.service + service_path="/etc/systemd/system/$EVERNODE_AUTO_UPDATE_SERVICE.service" + rm -f $service_path - # Reload the systemd daemon. - systemctl daemon-reload -} + # Reload the systemd daemon. + systemctl daemon-reload + } -function install_evernode() { - local upgrade=$1 + function install_evernode() { + local upgrade=$1 - # Get installer version (timestamp). We use this later to check for Evernode software updates. - local installer_version_timestamp=$(online_version_timestamp) - [ -z "$installer_version_timestamp" ] && echo "Online installer not found." && exit 1 + # Get installer version (timestamp). We use this later to check for Evernode software updates. + local installer_version_timestamp=$(online_version_timestamp) + [ -z "$installer_version_timestamp" ] && echo "Online installer not found." && exit 1 - local tmp=$(mktemp -d) - cd $tmp - curl --silent -L $installer_url --output installer.tgz - tar zxf $tmp/installer.tgz --strip-components=1 - rm installer.tgz + local tmp=$(mktemp -d) + cd $tmp + curl --silent -L $installer_url --output installer.tgz + tar zxf $tmp/installer.tgz --strip-components=1 + rm installer.tgz - set -o pipefail # We need installer exit code to detect failures (ignore the tee pipe exit code). - mkdir -p $log_dir - logfile="$log_dir/installer-$(date +%s).log" + set -o pipefail # We need installer exit code to detect failures (ignore the tee pipe exit code). + mkdir -p $log_dir + logfile="$log_dir/installer-$(date +%s).log" - if [ "$upgrade" == "0" ] ; then - echo "Installing other prerequisites..." - ! ./prereq.sh $cgrulesengd_service 2>&1 \ - | tee -a $logfile | stdbuf --output=L grep "STAGE" | cut -d ' ' -f 2- && install_failure - fi - - # Create evernode cli alias at the begining. - # So, if the installation attempt failed user can uninstall the failed installation using evernode commands. - ! create_evernode_alias && install_failure - - # Currently the domain address saved only in account_info and an empty value in Hook states. - # Set description to empty value ('_' will be treated as empty) - description="_" - - echo "Installing Sashimono..." - - init_setup_helpers - registry_address=$(exec_jshelper access-evernode-cfg $rippled_server $EVERNODE_GOVERNOR_ADDRESS registryAddress) - - # Filter logs with STAGE prefix and ommit the prefix when echoing. - # If STAGE log contains -p arg, move the cursor to previous log line and overwrite the log. - ! UPGRADE=$upgrade EVERNODE_REGISTRY_ADDRESS=$registry_address OPERATION=$operation ./sashimono-install.sh $inetaddr $init_peer_port $init_user_port $countrycode $alloc_instcount \ - $alloc_cpu $alloc_ramKB $alloc_swapKB $alloc_diskKB $lease_amount $rippled_server $xrpl_account_address $xrpl_account_secret_path $email_address \ - $tls_key_file $tls_cert_file $tls_cabundle_file $description $ipv6_subnet $ipv6_net_interface $extra_txn_fee 2>&1 \ - | tee -a $logfile | stdbuf --output=L grep "STAGE\|ERROR" \ - | while read line ; do [[ $line =~ ^STAGE[[:space:]]-p(.*)$ ]] && echo -e \\e[1A\\e[K"${line:9}" || echo ${line:6} ; done \ - && remove_evernode_alias && install_failure - - # Enable the Evernode Auto Updater Service. - if [ "$enable_auto_update" = true ]; then - stage "Configuring auto updater service" - enable_evernode_auto_updater - fi - - set +o pipefail - - rm -r $tmp - - # Write the verison timestamp to a file for later updated version comparison. - echo $installer_version_timestamp > $SASHIMONO_DATA/$installer_version_timestamp_file -} - -function check_exisiting_contracts() { - - local upgrade=$1 - - # Check the condition of existing contract instances. - local users=$(cut -d: -f1 /etc/passwd | grep "^$SASHIUSER_PREFIX" | sort) - readarray -t userarr <<<"$users" - local sashiusers=() - for user in "${userarr[@]}"; do - [ ${#user} -lt 24 ] || [ ${#user} -gt 32 ] || [[ ! "$user" =~ ^$SASHIUSER_PREFIX[0-9]+$ ]] && continue - sashiusers+=("$user") - done - local ucount=${#sashiusers[@]} - - if [ "$upgrade" == "0" ] ; then - $interactive && [ $ucount -gt 0 ] && ! confirm "This will delete $ucount contract instances. \n\nDo you still want to continue?" && exit 1 - ! $interactive && echo "$ucount contract instances will be deleted." - fi -} - -function uninstall_evernode() { - - local upgrade=$1 - - if ! $transfer ; then - [ "$upgrade" == "0" ] && echo "Uninstalling..." || echo "Uninstalling for upgrade..." - ! UPGRADE=$upgrade TRANSFER=0 $SASHIMONO_BIN/sashimono-uninstall.sh $2 && uninstall_failure - - # Remove the Evernode Auto Updater Service. - [ "$upgrade" == "0" ] && systemctl list-unit-files | grep -q $EVERNODE_AUTO_UPDATE_SERVICE.service && remove_evernode_auto_updater - else - echo "Intiating Transfer..." - echo "Uninstalling for transfer..." - ! UPGRADE=$upgrade TRANSFER=1 $SASHIMONO_BIN/sashimono-uninstall.sh $2 && uninstall_failure - fi - # Remove the evernode alias at the end. - # So, if the uninstallation failed user can try uninstall again with evernode commands. - remove_evernode_alias -} - -function update_evernode() { - echo "Checking for updates..." - local latest_installer_script_version=$(online_version_timestamp) - [ -z "$latest_installer_script_version" ] && echo "Could not check for updates. Online installer not found." && exit 1 - - local current_installer_script_version=$(cat $SASHIMONO_DATA/$installer_version_timestamp_file) - [ "$latest_installer_script_version" == "$current_installer_script_version" ] && echo "Your $evernode installation is up to date." && exit 0 - - echo "New $evernode update available. Setup will re-install $evernode with updated software. Your account and contract instances will be preserved." - $interactive && ! confirm "\nDo you want to install the update?" && exit 1 - - echo "Starting upgrade..." - # Alias for setup.sh is created during 'install_evernode' too. - # If only the setup.sh is updated but not the installer, then the alias should be created again. - if [ "$latest_installer_script_version" != "$current_installer_script_version" ] ; then - uninstall_evernode 1 - install_evernode 1 - fi - - rm -r $setup_helper_dir >/dev/null 2>&1 - - echo "Upgrade complete." -} - -function init_evernode_transfer() { - - if ! sudo -u $MB_XRPL_USER MB_DATA_DIR=$MB_XRPL_DATA node $MB_XRPL_BIN transfer $transferee_address && - [ "$force" != "-f" ] && [ -f $mb_service_path ]; then - ! confirm "Evernode transfer initiation was failed. Still do you want to continue the unistallation?" && echo "Aborting unistallation. Try again later." && exit 1 - echo "Continuing uninstallation..." - fi - -} - -function create_log() { - tempfile=$(mktemp /tmp/evernode.XXXXXXXXX.log) - { - echo "System:" - uname -r - lsb_release -a - echo "" - echo "sa.cfg:" - cat "$SASHIMONO_DATA/sa.cfg" - echo "" - echo "mb-xrpl.cfg:" - cat "$MB_XRPL_DATA/mb-xrpl.cfg" - echo "" - echo "Sashimono log:" - journalctl -u sashimono-agent.service | tail -n 200 - echo "" - echo "Message board log:" - sudo -u sashimbxrpl bash -c journalctl --user -u sashimono-mb-xrpl | tail -n 200 - echo "" - echo "Auto updater service log:" - journalctl -u evernode-auto-update | tail -n 200 - } > "$tempfile" 2>&1 - echo "Evernode log saved to $tempfile" -} - -# Create a copy of this same script as a command. -function create_evernode_alias() { - ! curl -fsSL $setup_script_url --output $evernode_alias >> $logfile 2>&1 && echo "Error in creating alias." && return 1 - ! chmod +x $evernode_alias >> $logfile 2>&1 && echo "Error in changing permission for the alias." && return 1 - return 0 -} - -function remove_evernode_alias() { - rm $evernode_alias -} - -function check_installer_pending_finish() { - if [ -f /run/reboot-required.pkgs ] && [ -n "$(grep sashimono /run/reboot-required.pkgs)" ]; then - echo "Your system needs to be rebooted in order to complete Sashimono installation." - confirm "Reboot now?" && reboot - return 0 - else - # If reboot not required, check whether re-login is required in case the setup was run with sudo. - # This is because the user account gets added to sashiadmin group and re-login is needed for group permission to apply. - # without this, user cannot run "sashi" cli commands without sudo. - if [ "$mode" == "install" ] && [ -n "$SUDO_USER" ] ; then - echo "You need to logout and log back in, to complete Sashimono installation." - return 0 - else - return 1 + if [ "$upgrade" == "0" ]; then + echo "Installing other prerequisites..." + ! ./prereq.sh $cgrulesengd_service 2>&1 | + tee -a $logfile | stdbuf --output=L grep "STAGE" | cut -d ' ' -f 2- && install_failure fi - fi -} -function reg_info() { - local reg_info=$( MB_DATA_DIR=$MB_XRPL_DATA node $MB_XRPL_BIN reginfo || echo ERROR) - local error=$(echo "$reg_info" | tail -1) - [ "$error" == "ERROR" ] && echo "${reg_info/ERROR/""}" && exit 1 + # Create evernode cli alias at the begining. + # So, if the installation attempt failed user can uninstall the failed installation using evernode commands. + ! create_evernode_alias && install_failure - # Get raddress from first line. - local address_line=$(echo "$reg_info" | head -1) - local host_address=$( echo "$address_line" | awk -F : ' { print $2 } ') - echo -e "\n$address_line\n" - generate_qrcode "$host_address" + # Currently the domain address saved only in account_info and an empty value in Hook states. + # Set description to empty value ('_' will be treated as empty) + description="_" - # Remove first line and print. - echo -e "\n${reg_info/$address_line/""}" + echo "Installing Sashimono..." - echo -e "NOTE: The host is marked as active after sending the first heartbeat.\n" + init_setup_helpers + registry_address=$(exec_jshelper access-evernode-cfg $rippled_server $EVERNODE_GOVERNOR_ADDRESS registryAddress) - local sashimono_agent_status=$(systemctl is-active sashimono-agent.service) - local mb_user_id=$(id -u "$MB_XRPL_USER") - local mb_user_runtime_dir="/run/user/$mb_user_id" - local sashimono_mb_xrpl_status=$(sudo -u "$MB_XRPL_USER" XDG_RUNTIME_DIR="$mb_user_runtime_dir" systemctl --user is-active $MB_XRPL_SERVICE) - echo "Sashimono agent status: $sashimono_agent_status" - echo "Sashimono message board status: $sashimono_mb_xrpl_status" - echo -e "\nYour account details are stored in $MB_XRPL_DATA/mb-xrpl.cfg" -} + # Filter logs with STAGE prefix and ommit the prefix when echoing. + # If STAGE log contains -p arg, move the cursor to previous log line and overwrite the log. + ! UPGRADE=$upgrade EVERNODE_REGISTRY_ADDRESS=$registry_address OPERATION=$operation ./sashimono-install.sh $inetaddr $init_peer_port $init_user_port $countrycode $alloc_instcount \ + $alloc_cpu $alloc_ramKB $alloc_swapKB $alloc_diskKB $lease_amount $rippled_server $xrpl_account_address $xrpl_account_secret_path $email_address \ + $tls_key_file $tls_cert_file $tls_cabundle_file $description $ipv6_subnet $ipv6_net_interface $extra_txn_fee 2>&1 | + tee -a $logfile | stdbuf --output=L grep "STAGE\|ERROR" | + while read line; do [[ $line =~ ^STAGE[[:space:]]-p(.*)$ ]] && echo -e \\e[1A\\e[K"${line:9}" || echo ${line:6}; done && + remove_evernode_alias && install_failure -function apply_ssl() { - [ "$EUID" -ne 0 ] && echo "Please run with root privileges (sudo)." && exit 1 - - local tls_key_file=$1 - local tls_cert_file=$2 - local tls_cabundle_file=$3 + # Enable the Evernode Auto Updater Service. + if [ "$enable_auto_update" = true ]; then + stage "Configuring auto updater service" + enable_evernode_auto_updater + fi - ([ ! -f "$tls_key_file" ] || [ ! -f "$tls_cert_file" ] || \ - ([ "$tls_cabundle_file" != "" ] && [ ! -f "$tls_cabundle_file" ])) && - echo -e "One or more invalid files provided.\nusage: applyssl " && exit 1 + set +o pipefail - echo "Applying new SSL certificates for $evernode" - echo "Key: $tls_key_file" && cp $tls_key_file $SASHIMONO_DATA/contract_template/cfg/tlskey.pem || exit 1 - echo "Cert: $tls_cert_file" && cp $tls_cert_file $SASHIMONO_DATA/contract_template/cfg/tlscert.pem || exit 1 - # ca bundle is optional. - [ "$tls_cabundle_file" != "" ] && echo "CA bundle: $tls_cabundle_file" && (cat $tls_cabundle_file >> $SASHIMONO_DATA/contract_template/cfg/tlscert.pem || exit 1) + rm -r $tmp - sashi list | jq -rc '.[]' | while read -r inst; do \ - local instuser=$(echo $inst | jq -r '.user'); \ - local instname=$(echo $inst | jq -r '.name'); \ - echo -e "\nStopping contract instance $instname" && sashi stop -n $instname && \ - echo "Updating SSL certificates" && \ - cp $SASHIMONO_DATA/contract_template/cfg/tlskey.pem $SASHIMONO_DATA/contract_template/cfg/tlscert.pem /home/$instuser/$instname/cfg/ && \ - chmod 644 /home/$instuser/$instname/cfg/tlscert.pem && chmod 600 /home/$instuser/$instname/cfg/tlskey.pem && \ - chown -R $instuser:$instuser /home/$instuser/$instname/cfg/*.pem && \ - echo -e "Starting contract instance $instname" && sashi start -n $instname; \ - done + # Write the verison timestamp to a file for later updated version comparison. + echo $installer_version_timestamp >$SASHIMONO_DATA/$installer_version_timestamp_file + } - echo "Done." -} + function check_exisiting_contracts() { -function reconfig_sashi() { - echomult "Configuaring sashimono...\n" + local upgrade=$1 - ! $SASHIMONO_BIN/sagent reconfig $SASHIMONO_DATA $alloc_instcount $alloc_cpu $alloc_ramKB $alloc_swapKB $alloc_diskKB && - echomult "There was an error in updating sashimono configuration." && return 1 - - # Update cgroup allocations. - ( [[ $alloc_ramKB -gt 0 ]] || [[ $alloc_swapKB -gt 0 ]] || [[ $alloc_instcount -gt 0 ]] ) && - echomult "Updating the cgroup configuration..." && - ! $SASHIMONO_BIN/user-cgcreate.sh $SASHIMONO_DATA && echomult "Error occured while upgrading cgroup allocations" && return 1 - - # Update disk quotas. - if ( [[ $alloc_diskKB -gt 0 ]] || [[ $alloc_instcount -gt 0 ]] ) ; then - echomult "Updating the disk quotas..." - - users=$(cut -d: -f1 /etc/passwd | grep "^$SASHIUSER_PREFIX" | sort) + # Check the condition of existing contract instances. + local users=$(cut -d: -f1 /etc/passwd | grep "^$SASHIUSER_PREFIX" | sort) readarray -t userarr <<<"$users" - sashiusers=() + local sashiusers=() for user in "${userarr[@]}"; do [ ${#user} -lt 24 ] || [ ${#user} -gt 32 ] || [[ ! "$user" =~ ^$SASHIUSER_PREFIX[0-9]+$ ]] && continue sashiusers+=("$user") done + local ucount=${#sashiusers[@]} - max_storage_kbytes=$(jq '.system.max_storage_kbytes' $saconfig) - max_instance_count=$(jq '.system.max_instance_count' $saconfig) - disk=$(expr $max_storage_kbytes / $max_instance_count) - ucount=${#sashiusers[@]} - if [ $ucount -gt 0 ]; then - for user in "${sashiusers[@]}"; do - setquota -g -F vfsv0 "$user" "$disk" "$disk" 0 0 / - done + if [ "$upgrade" == "0" ]; then + $interactive && [ $ucount -gt 0 ] && ! confirm "This will delete $ucount contract instances. \n\nDo you still want to continue?" && exit 1 + ! $interactive && echo "$ucount contract instances will be deleted." fi - fi + } - return 0 -} + function uninstall_evernode() { -function reconfig_mb() { - echomult "Configuaring message board...\n" + local upgrade=$1 - ! sudo -u $MB_XRPL_USER MB_DATA_DIR=$MB_XRPL_DATA node $MB_XRPL_BIN reconfig $lease_amount $alloc_instcount $rippled_server $ipv6_subnet $ipv6_net_interface $extra_txn_fee && - echo "There was an error in updating message board configuration." && return 1 - return 0 -} + if ! $transfer; then + [ "$upgrade" == "0" ] && echo "Uninstalling..." || echo "Uninstalling for upgrade..." + ! UPGRADE=$upgrade TRANSFER=0 $SASHIMONO_BIN/sashimono-uninstall.sh $2 && uninstall_failure -function config() { - [ "$EUID" -ne 0 ] && echo "Please run with root privileges (sudo)." && exit 1 + # Remove the Evernode Auto Updater Service. + [ "$upgrade" == "0" ] && systemctl list-unit-files | grep -q $EVERNODE_AUTO_UPDATE_SERVICE.service && remove_evernode_auto_updater + else + echo "Intiating Transfer..." + echo "Uninstalling for transfer..." + ! UPGRADE=$upgrade TRANSFER=1 $SASHIMONO_BIN/sashimono-uninstall.sh $2 && uninstall_failure + fi + # Remove the evernode alias at the end. + # So, if the uninstallation failed user can try uninstall again with evernode commands. + remove_evernode_alias + } - alloc_instcount=0 - alloc_cpu=0 - alloc_ramKB=0 - alloc_swapKB=0 - alloc_diskKB=0 - lease_amount=0 - rippled_server='-' - ipv6_subnet='-' - ipv6_net_interface='-' - extra_txn_fee='-' + function update_evernode() { + echo "Checking for updates..." + local latest_installer_script_version=$(online_version_timestamp) + [ -z "$latest_installer_script_version" ] && echo "Could not check for updates. Online installer not found." && exit 1 - local saconfig="$SASHIMONO_DATA/sa.cfg" - local max_instance_count=$(jq '.system.max_instance_count' $saconfig) - local max_mem_kbytes=$(jq '.system.max_mem_kbytes' $saconfig) - local max_swap_kbytes=$(jq '.system.max_swap_kbytes' $saconfig) - local max_storage_kbytes=$(jq '.system.max_storage_kbytes' $saconfig) + local current_installer_script_version=$(cat $SASHIMONO_DATA/$installer_version_timestamp_file) + [ "$latest_installer_script_version" == "$current_installer_script_version" ] && echo "Your $evernode installation is up to date." && exit 0 - local mbconfig="$MB_XRPL_DATA/mb-xrpl.cfg" - local cfg_lease_amount=$(jq '.xrpl.leaseAmount' $mbconfig) - local cfg_rippled_server=$(jq -r '.xrpl.rippledServer' $mbconfig) - local cfg_extra_txn_fee=$(jq '.xrpl.affordableExtraFee' $mbconfig) + echo "New $evernode update available. Setup will re-install $evernode with updated software. Your account and contract instances will be preserved." + $interactive && ! confirm "\nDo you want to install the update?" && exit 1 - local cfg_ipv6_subnet=$(jq -r '.networking.ipv6.subnet' $mbconfig) - local cfg_ipv6_net_interface=$(jq -r '.networking.ipv6.interface' $mbconfig) + echo "Starting upgrade..." + # Alias for setup.sh is created during 'install_evernode' too. + # If only the setup.sh is updated but not the installer, then the alias should be created again. + if [ "$latest_installer_script_version" != "$current_installer_script_version" ]; then + uninstall_evernode 1 + install_evernode 1 + fi - local update_sashi=0 - local update_mb=0 + rm -r $setup_helper_dir >/dev/null 2>&1 - local sub_mode=${1} - local occupied_instance_count=$(sashi list | jq length) + echo "Upgrade complete." + } - if [ "$sub_mode" == "resources" ] ; then + function init_evernode_transfer() { - local ramMB=${2} # memory to allocate for contract instances. - local swapMB=${3} # Swap to allocate for contract instances. - local diskMB=${4} # Disk space to allocate for contract instances. - local instcount=${5} # Total contract instance count. + if ! sudo -u $MB_XRPL_USER MB_DATA_DIR=$MB_XRPL_DATA node $MB_XRPL_BIN transfer $transferee_address && + [ "$force" != "-f" ] && [ -f $mb_service_path ]; then + ! confirm "Evernode transfer initiation was failed. Still do you want to continue the unistallation?" && echo "Aborting unistallation. Try again later." && exit 1 + echo "Continuing uninstallation..." + fi - [ -z $ramMB ] && [ -z $swapMB ] && [ -z $diskMB ] && [ -z $instcount ] && - echomult "Your current resource allocation is: + } + + function create_log() { + tempfile=$(mktemp /tmp/evernode.XXXXXXXXX.log) + { + echo "System:" + uname -r + lsb_release -a + echo "" + echo "sa.cfg:" + cat "$SASHIMONO_DATA/sa.cfg" + echo "" + echo "mb-xrpl.cfg:" + cat "$MB_XRPL_CONFIG" + echo "" + echo "Sashimono log:" + journalctl -u sashimono-agent.service | tail -n 200 + echo "" + echo "Message board log:" + sudo -u sashimbxrpl bash -c journalctl --user -u sashimono-mb-xrpl | tail -n 200 + echo "" + echo "Auto updater service log:" + journalctl -u evernode-auto-update | tail -n 200 + } >"$tempfile" 2>&1 + echo "Evernode log saved to $tempfile" + } + + # Create a copy of this same script as a command. + function create_evernode_alias() { + ! curl -fsSL $setup_script_url --output $evernode_alias >>$logfile 2>&1 && echo "Error in creating alias." && return 1 + ! chmod +x $evernode_alias >>$logfile 2>&1 && echo "Error in changing permission for the alias." && return 1 + return 0 + } + + function remove_evernode_alias() { + rm $evernode_alias + } + + function check_installer_pending_finish() { + if [ -f /run/reboot-required.pkgs ] && [ -n "$(grep sashimono /run/reboot-required.pkgs)" ]; then + echo "Your system needs to be rebooted in order to complete Sashimono installation." + confirm "Reboot now?" && reboot + return 0 + else + # If reboot not required, check whether re-login is required in case the setup was run with sudo. + # This is because the user account gets added to sashiadmin group and re-login is needed for group permission to apply. + # without this, user cannot run "sashi" cli commands without sudo. + if [ "$mode" == "install" ] && [ -n "$SUDO_USER" ]; then + echo "You need to logout and log back in, to complete Sashimono installation." + return 0 + else + return 1 + fi + fi + } + + function reg_info() { + local reg_info=$(MB_DATA_DIR=$MB_XRPL_DATA node $MB_XRPL_BIN reginfo || echo ERROR) + local error=$(echo "$reg_info" | tail -1) + [ "$error" == "ERROR" ] && echo "${reg_info/ERROR/""}" && exit 1 + + # Get raddress from first line. + local address_line=$(echo "$reg_info" | head -1) + local host_address=$(echo "$address_line" | awk -F : ' { print $2 } ') + echo -e "\n$address_line\n" + generate_qrcode "$host_address" + + # Remove first line and print. + echo -e "\n${reg_info/$address_line/""}" + + echo -e "NOTE: The host is marked as active after sending the first heartbeat.\n" + + local sashimono_agent_status=$(systemctl is-active sashimono-agent.service) + local mb_user_id=$(id -u "$MB_XRPL_USER") + local mb_user_runtime_dir="/run/user/$mb_user_id" + local sashimono_mb_xrpl_status=$(sudo -u "$MB_XRPL_USER" XDG_RUNTIME_DIR="$mb_user_runtime_dir" systemctl --user is-active $MB_XRPL_SERVICE) + echo "Sashimono agent status: $sashimono_agent_status" + echo "Sashimono message board status: $sashimono_mb_xrpl_status" + echo -e "\nYour account details are stored in $MB_XRPL_DATA/mb-xrpl.cfg" + } + + function apply_ssl() { + [ "$EUID" -ne 0 ] && echo "Please run with root privileges (sudo)." && exit 1 + + local tls_key_file=$1 + local tls_cert_file=$2 + local tls_cabundle_file=$3 + + ([ ! -f "$tls_key_file" ] || [ ! -f "$tls_cert_file" ] || + ([ "$tls_cabundle_file" != "" ] && [ ! -f "$tls_cabundle_file" ])) && + echo -e "One or more invalid files provided.\nusage: applyssl " && exit 1 + + echo "Applying new SSL certificates for $evernode" + echo "Key: $tls_key_file" && cp $tls_key_file $SASHIMONO_DATA/contract_template/cfg/tlskey.pem || exit 1 + echo "Cert: $tls_cert_file" && cp $tls_cert_file $SASHIMONO_DATA/contract_template/cfg/tlscert.pem || exit 1 + # ca bundle is optional. + [ "$tls_cabundle_file" != "" ] && echo "CA bundle: $tls_cabundle_file" && (cat $tls_cabundle_file >>$SASHIMONO_DATA/contract_template/cfg/tlscert.pem || exit 1) + + sashi list | jq -rc '.[]' | while read -r inst; do + local instuser=$(echo $inst | jq -r '.user') + local instname=$(echo $inst | jq -r '.name') + echo -e "\nStopping contract instance $instname" && sashi stop -n $instname && + echo "Updating SSL certificates" && + cp $SASHIMONO_DATA/contract_template/cfg/tlskey.pem $SASHIMONO_DATA/contract_template/cfg/tlscert.pem /home/$instuser/$instname/cfg/ && + chmod 644 /home/$instuser/$instname/cfg/tlscert.pem && chmod 600 /home/$instuser/$instname/cfg/tlskey.pem && + chown -R $instuser:$instuser /home/$instuser/$instname/cfg/*.pem && + echo -e "Starting contract instance $instname" && sashi start -n $instname + done + + echo "Done." + } + + function reconfig_sashi() { + echomult "Configuaring sashimono...\n" + + ! $SASHIMONO_BIN/sagent reconfig $SASHIMONO_DATA $alloc_instcount $alloc_cpu $alloc_ramKB $alloc_swapKB $alloc_diskKB && + echomult "There was an error in updating sashimono configuration." && return 1 + + # Update cgroup allocations. + ([[ $alloc_ramKB -gt 0 ]] || [[ $alloc_swapKB -gt 0 ]] || [[ $alloc_instcount -gt 0 ]]) && + echomult "Updating the cgroup configuration..." && + ! $SASHIMONO_BIN/user-cgcreate.sh $SASHIMONO_DATA && echomult "Error occured while upgrading cgroup allocations" && return 1 + + # Update disk quotas. + if ([[ $alloc_diskKB -gt 0 ]] || [[ $alloc_instcount -gt 0 ]]); then + echomult "Updating the disk quotas..." + + users=$(cut -d: -f1 /etc/passwd | grep "^$SASHIUSER_PREFIX" | sort) + readarray -t userarr <<<"$users" + sashiusers=() + for user in "${userarr[@]}"; do + [ ${#user} -lt 24 ] || [ ${#user} -gt 32 ] || [[ ! "$user" =~ ^$SASHIUSER_PREFIX[0-9]+$ ]] && continue + sashiusers+=("$user") + done + + max_storage_kbytes=$(jq '.system.max_storage_kbytes' $saconfig) + max_instance_count=$(jq '.system.max_instance_count' $saconfig) + disk=$(expr $max_storage_kbytes / $max_instance_count) + ucount=${#sashiusers[@]} + if [ $ucount -gt 0 ]; then + for user in "${sashiusers[@]}"; do + setquota -g -F vfsv0 "$user" "$disk" "$disk" 0 0 / + done + fi + fi + + return 0 + } + + function reconfig_mb() { + echomult "Configuaring message board...\n" + + ! sudo -u $MB_XRPL_USER MB_DATA_DIR=$MB_XRPL_DATA node $MB_XRPL_BIN reconfig $lease_amount $alloc_instcount $rippled_server $ipv6_subnet $ipv6_net_interface $extra_txn_fee && + echo "There was an error in updating message board configuration." && return 1 + return 0 + } + + function config() { + [ "$EUID" -ne 0 ] && echo "Please run with root privileges (sudo)." && exit 1 + + alloc_instcount=0 + alloc_cpu=0 + alloc_ramKB=0 + alloc_swapKB=0 + alloc_diskKB=0 + lease_amount=0 + rippled_server='-' + ipv6_subnet='-' + ipv6_net_interface='-' + extra_txn_fee='-' + + local saconfig="$SASHIMONO_DATA/sa.cfg" + local max_instance_count=$(jq '.system.max_instance_count' $saconfig) + local max_mem_kbytes=$(jq '.system.max_mem_kbytes' $saconfig) + local max_swap_kbytes=$(jq '.system.max_swap_kbytes' $saconfig) + local max_storage_kbytes=$(jq '.system.max_storage_kbytes' $saconfig) + + local mbconfig="$MB_XRPL_CONFIG" + local cfg_lease_amount=$(jq '.xrpl.leaseAmount' $mbconfig) + local cfg_rippled_server=$(jq -r '.xrpl.rippledServer' $mbconfig) + local cfg_extra_txn_fee=$(jq '.xrpl.affordableExtraFee' $mbconfig) + + local cfg_ipv6_subnet=$(jq -r '.networking.ipv6.subnet' $mbconfig) + local cfg_ipv6_net_interface=$(jq -r '.networking.ipv6.interface' $mbconfig) + + local update_sashi=0 + local update_mb=0 + + local sub_mode=${1} + local occupied_instance_count=$(sashi list | jq length) + + if [ "$sub_mode" == "resources" ]; then + + local ramMB=${2} # memory to allocate for contract instances. + local swapMB=${3} # Swap to allocate for contract instances. + local diskMB=${4} # Disk space to allocate for contract instances. + local instcount=${5} # Total contract instance count. + + [ -z $ramMB ] && [ -z $swapMB ] && [ -z $diskMB ] && [ -z $instcount ] && + echomult "Your current resource allocation is: \n Memory: $(GB $max_mem_kbytes) \n Swap: $(GB $max_swap_kbytes) \n Disk space: $(GB $max_storage_kbytes) \n Instance count: $max_instance_count\n" && exit 0 + local help_text="Usage: evernode config resources | evernode config resources \n" + [ ! -z $ramMB ] && [[ $ramMB != 0 ]] && ! validate_positive_decimal $ramMB && + echomult "Invalid memory size.\n $help_text" && exit 1 + [ ! -z $swapMB ] && [[ $swapMB != 0 ]] && ! validate_positive_decimal $swapMB && + echomult "Invalid swap size.\n $help_text" && exit 1 + [ ! -z $diskMB ] && [[ $diskMB != 0 ]] && ! validate_positive_decimal $diskMB && + echomult "Invalid disk size.\n $help_text" && exit 1 + [ ! -z $instcount ] && [[ $instcount != 0 ]] && ! validate_positive_decimal $instcount && + echomult "Invalid instance count.\n $help_text" && exit 1 - local help_text="Usage: evernode config resources | evernode config resources \n" - [ ! -z $ramMB ] && [[ $ramMB != 0 ]] && ! validate_positive_decimal $ramMB && - echomult "Invalid memory size.\n $help_text" && exit 1 - [ ! -z $swapMB ] && [[ $swapMB != 0 ]] && ! validate_positive_decimal $swapMB && - echomult "Invalid swap size.\n $help_text" && exit 1 - [ ! -z $diskMB ] && [[ $diskMB != 0 ]] && ! validate_positive_decimal $diskMB && - echomult "Invalid disk size.\n $help_text" && exit 1 - [ ! -z $instcount ] && [[ $instcount != 0 ]] && ! validate_positive_decimal $instcount && - echomult "Invalid instance count.\n $help_text" && exit 1 + [ -z $instcount ] && instcount=0 + alloc_instcount=$instcount + alloc_ramKB=$((ramMB * 1000)) + alloc_swapKB=$((swapMB * 1000)) + alloc_diskKB=$((diskMB * 1000)) - [ -z $instcount ] && instcount=0 - alloc_instcount=$instcount - alloc_ramKB=$(( ramMB * 1000 )) - alloc_swapKB=$(( swapMB * 1000 )) - alloc_diskKB=$(( diskMB * 1000 )) + ( ([[ $alloc_instcount -eq 0 ]] || [[ $max_instance_count == $alloc_instcount ]]) && + ([[ $alloc_ramKB -eq 0 ]] || [[ $max_mem_kbytes == $alloc_ramKB ]]) && + ([[ $alloc_swapKB -eq 0 ]] || [[ $max_swap_kbytes == $alloc_swapKB ]]) && + ([[ $alloc_diskKB -eq 0 ]] || [[ $max_storage_kbytes == $alloc_diskKB ]])) && + echomult "Resource configuration values are already configured!\n" && exit 0 - ( ( [[ $alloc_instcount -eq 0 ]] || [[ $max_instance_count == $alloc_instcount ]] ) && - ( [[ $alloc_ramKB -eq 0 ]] || [[ $max_mem_kbytes == $alloc_ramKB ]] ) && - ( [[ $alloc_swapKB -eq 0 ]] || [[ $max_swap_kbytes == $alloc_swapKB ]] ) && - ( [[ $alloc_diskKB -eq 0 ]] || [[ $max_storage_kbytes == $alloc_diskKB ]] ) ) && - echomult "Resource configuration values are already configured!\n" && exit 0 + echomult "Using allocation" + [[ $alloc_ramKB -gt 0 ]] && echomult "$(GB $alloc_ramKB) memory" + [[ $alloc_swapKB -gt 0 ]] && echomult "$(GB $alloc_swapKB) Swap" + [[ $alloc_diskKB -gt 0 ]] && echomult "$(GB $alloc_diskKB) disk space" + [[ $alloc_instcount -gt 0 ]] && echomult "Distributed among $alloc_instcount contract instances" - echomult "Using allocation" - [[ $alloc_ramKB -gt 0 ]] && echomult "$(GB $alloc_ramKB) memory" - [[ $alloc_swapKB -gt 0 ]] && echomult "$(GB $alloc_swapKB) Swap" - [[ $alloc_diskKB -gt 0 ]] && echomult "$(GB $alloc_diskKB) disk space" - [[ $alloc_instcount -gt 0 ]] && echomult "Distributed among $alloc_instcount contract instances" + update_sashi=1 + [[ $alloc_instcount -gt 0 ]] && update_mb=1 - update_sashi=1 - [[ $alloc_instcount -gt 0 ]] && update_mb=1 + elif [ "$sub_mode" == "leaseamt" ]; then - elif [ "$sub_mode" == "leaseamt" ] ; then + local amount=${2} # Contract instance lease amount in EVRs. + [ -z $amount ] && echomult "Your current lease amount is: $cfg_lease_amount EVRs.\n" && exit 0 - local amount=${2} # Contract instance lease amount in EVRs. - [ -z $amount ] && echomult "Your current lease amount is: $cfg_lease_amount EVRs.\n" && exit 0 + ! validate_positive_decimal $amount && + echomult "Invalid lease amount.\n Usage: evernode config leaseamt | evernode config leaseamt \n" && + exit 1 + lease_amount=$amount + [[ $cfg_lease_amount == $lease_amount ]] && echomult "Lease amount is already configured!\n" && exit 0 + echomult "Using lease amount $lease_amount EVRs." - ! validate_positive_decimal $amount && - echomult "Invalid lease amount.\n Usage: evernode config leaseamt | evernode config leaseamt \n" && - exit 1 - lease_amount=$amount - [[ $cfg_lease_amount == $lease_amount ]] && echomult "Lease amount is already configured!\n" && exit 0 + update_mb=1 - echomult "Using lease amount $lease_amount EVRs." + elif [ "$sub_mode" == "rippled" ]; then - update_mb=1 + local server=${2} # Rippled server URL + [ -z $server ] && echomult "Your current rippled server is: $cfg_rippled_server\n" && exit 0 - elif [ "$sub_mode" == "rippled" ] ; then - - local server=${2} # Rippled server URL - [ -z $server ] && echomult "Your current rippled server is: $cfg_rippled_server\n" && exit 0 + ! validate_rippled_url $server && + echomult "\nUsage: evernode config rippled | evernode config rippled \n" && + exit 1 + rippled_server=$server + [[ $cfg_rippled_server == $rippled_server ]] && echomult "Rippled server is already configured!\n" && exit 0 - ! validate_rippled_url $server && - echomult "\nUsage: evernode config rippled | evernode config rippled \n" && - exit 1 - rippled_server=$server - [[ $cfg_rippled_server == $rippled_server ]] && echomult "Rippled server is already configured!\n" && exit 0 + echomult "Using the rippled address '$rippled_server'." - echomult "Using the rippled address '$rippled_server'." + update_mb=1 - update_mb=1 + elif [ "$sub_mode" == "email" ]; then - elif [ "$sub_mode" == "email" ] ; then - - local email_address=${2} # Email address + local email_address=${2} # Email address - local cfg_host_address=$(jq -r '.xrpl.address' $mbconfig) + local cfg_host_address=$(jq -r '.xrpl.address' $mbconfig) - [ ! -z $email_address ] && ! validate_email_address $email_address && - echomult "\nUsage: evernode config email | evernode config email \n" && - exit 1 + [ ! -z $email_address ] && ! validate_email_address $email_address && + echomult "\nUsage: evernode config email | evernode config email \n" && + exit 1 - # Get info of the host. - local host_info=$(sudo -u $MB_XRPL_USER MB_DATA_DIR=$MB_XRPL_DATA node $MB_XRPL_BIN hostinfo) || exit 1 - local cur_email_address=$(echo $host_info | jq -r '.email') - - [ -z $email_address ] && echomult "Your current email address is: $cur_email_address\n" && exit 0 + # Get info of the host. + local host_info=$(sudo -u $MB_XRPL_USER MB_DATA_DIR=$MB_XRPL_DATA node $MB_XRPL_BIN hostinfo) || exit 1 + local cur_email_address=$(echo $host_info | jq -r '.email') - [[ $cur_email_address == $email_address ]] && echomult "Email address is already configured!\n" && exit 0 + [ -z $email_address ] && echomult "Your current email address is: $cur_email_address\n" && exit 0 - echomult "Using the email address '$email_address'." + [[ $cur_email_address == $email_address ]] && echomult "Email address is already configured!\n" && exit 0 - # If certbot installed, Sashimono might have been setup with letsencrypt certificates. - if command -v certbot &>/dev/null ; then - local inet_addr=$(jq -r '.hp.host_address' $saconfig) + echomult "Using the email address '$email_address'." - local key_file="/etc/letsencrypt/live/$inet_addr/privkey.pem" - local cert_file="/etc/letsencrypt/live/$inet_addr/fullchain.pem" - local renewed_key_file="$RENEWED_LINEAGE/privkey.pem" - local sashimono_key_file="$SASHIMONO_DATA/contract_template/cfg/tlskey.pem" + # If certbot installed, Sashimono might have been setup with letsencrypt certificates. + if command -v certbot &>/dev/null; then + local inet_addr=$(jq -r '.hp.host_address' $saconfig) - # If sashimono containes the letsencrypt certificates, Update them with new email. - if ( [ -f $key_file ] && cmp -s $key_file $sashimono_key_file ) || ( [ -f $renewed_key_file ] && cmp -s $renewed_key_file $sashimono_key_file ) ; then + local key_file="/etc/letsencrypt/live/$inet_addr/privkey.pem" + local cert_file="/etc/letsencrypt/live/$inet_addr/fullchain.pem" + local renewed_key_file="$RENEWED_LINEAGE/privkey.pem" + local sashimono_key_file="$SASHIMONO_DATA/contract_template/cfg/tlskey.pem" - # Get the current registration email if there's any. - local lenc_acc_email=$(certbot show_account 2>/dev/null | grep "Email contact:" | cut -d ':' -f2 | sed 's/ *//g') + # If sashimono containes the letsencrypt certificates, Update them with new email. + if ([ -f $key_file ] && cmp -s $key_file $sashimono_key_file) || ([ -f $renewed_key_file ] && cmp -s $renewed_key_file $sashimono_key_file); then - # If the emails are different, we need to update the letsencrypt email. - if [[ $lenc_acc_email != $email_address ]]; then - # If there are other certificates from this letsencrypt account, - # Complain that sashimono can't update the email since this account is used by other certificates. - local count=$(certbot certificates 2>/dev/null | grep "Certificate Name" | grep -v -c "$inet_addr") - [ $count -gt 0 ] && - echomult "Existing letsencrypt account with $lenc_acc_email has other certificates which are related to sashimono.\n + # Get the current registration email if there's any. + local lenc_acc_email=$(certbot show_account 2>/dev/null | grep "Email contact:" | cut -d ':' -f2 | sed 's/ *//g') + + # If the emails are different, we need to update the letsencrypt email. + if [[ $lenc_acc_email != $email_address ]]; then + # If there are other certificates from this letsencrypt account, + # Complain that sashimono can't update the email since this account is used by other certificates. + local count=$(certbot certificates 2>/dev/null | grep "Certificate Name" | grep -v -c "$inet_addr") + [ $count -gt 0 ] && + echomult "Existing letsencrypt account with $lenc_acc_email has other certificates which are related to sashimono.\n So letsencrypt email cannot be changed, Please use the same email or update the letsencrypt email with certbot." && - return 1 + return 1 + + ! certbot -n update_account -m $email_address && + echo "Could not update the letsencrypt email." && return 1 + fi - ! certbot -n update_account -m $email_address && - echo "Could not update the letsencrypt email." && return 1 fi - fi - fi - # Send update meassage to the registry. - ! sudo -u $MB_XRPL_USER MB_DATA_DIR=$MB_XRPL_DATA node $MB_XRPL_BIN update $email_address && - echo "Could not update host info." && return 1 + # Send update meassage to the registry. + ! sudo -u $MB_XRPL_USER MB_DATA_DIR=$MB_XRPL_DATA node $MB_XRPL_BIN update $email_address && + echo "Could not update host info." && return 1 - # We do not need to restart services for email update. - echomult "\nSuccessfully changed the email address!\n" && exit 0 + # We do not need to restart services for email update. + echomult "\nSuccessfully changed the email address!\n" && exit 0 - elif [ "$sub_mode" == "instance" ] ; then - local attribute=${2} + elif [ "$sub_mode" == "instance" ]; then + local attribute=${2} - if [ "$attribute" == "ipv6" ] ; then - ([ "$cfg_ipv6_subnet" != null ] && [ "$cfg_ipv6_net_interface" != null ]) && - echomult "You have already enabled IPv6 for instance outbound communication. + if [ "$attribute" == "ipv6" ]; then + ([ "$cfg_ipv6_subnet" != null ] && [ "$cfg_ipv6_net_interface" != null ]) && + echomult "You have already enabled IPv6 for instance outbound communication. \n Network Interface: $cfg_ipv6_net_interface \n Subnet: $cfg_ipv6_subnet" && - ! confirm "\nDo you want to go for a reconfiguration?" && return 0 + ! confirm "\nDo you want to go for a reconfiguration?" && return 0 - if ( [[ $occupied_instance_count -gt 0 ]] ); then - echomult "Could not proceed the reconfiguration as there are occupied instances." && exit 1 + if ([[ $occupied_instance_count -gt 0 ]]); then + echomult "Could not proceed the reconfiguration as there are occupied instances." && exit 1 + fi + + set_ipv6_subnet + if [[ "$ipv6_subnet" == "-" || "$ipv6_net_interface" == "-" ]]; then + echo -e "Could not proceed with provided details." && exit 1 + fi + + echo -e "Using $ipv6_subnet IPv6 subnet on $ipv6_net_interface for contract instances.\n" + update_mb=1 + + else + echomult "Invalid arguments.\n Usage: evernode config instance [ipv6]\n" && exit 1 fi - set_ipv6_subnet - if [[ "$ipv6_subnet" == "-" || "$ipv6_net_interface" == "-" ]]; then - echo -e "Could not proceed with provided details." && exit 1 - fi + elif [ "$sub_mode" == "extrafee" ]; then + + local fee=${2} # Affordable extra transaction fee to consider in txn failures. + [ -z $fee ] && echomult "Your affordable extra transaction fee: $cfg_extra_txn_fee XAH Drops.\n" && exit 0 + + ! validate_positive_decimal $fee && + echomult "Invalid fee amount.\n Usage: evernode config extrafee | evernode config extrafee \n" && + exit 1 + extra_txn_fee=$fee + [[ $cfg_extra_txn_fee == $extra_txn_fee ]] && echomult "Affordable extra transaction fee is already configured!\n" && exit 0 + + echomult "Using affordable extra transaction fee $extra_txn_fee XAH Drops." - echo -e "Using $ipv6_subnet IPv6 subnet on $ipv6_net_interface for contract instances.\n" update_mb=1 else - echomult "Invalid arguments.\n Usage: evernode config instance [ipv6]\n" && exit 1 + echomult "Invalid arguments.\n Usage: evernode config [resources|leaseamt|rippled|email|instance|extrafee] [arguments]\n" && exit 1 fi - elif [ "$sub_mode" == "extrafee" ] ; then - - local fee=${2} # Affordable extra transaction fee to consider in txn failures. - [ -z $fee ] && echomult "Your affordable extra transaction fee: $cfg_extra_txn_fee XAH Drops.\n" && exit 0 + local mb_user_id=$(id -u "$MB_XRPL_USER") + local mb_user_runtime_dir="/run/user/$mb_user_id" + local has_error=0 - ! validate_positive_decimal $fee && - echomult "Invalid fee amount.\n Usage: evernode config extrafee | evernode config extrafee \n" && - exit 1 - extra_txn_fee=$fee - [[ $cfg_extra_txn_fee == $extra_txn_fee ]] && echomult "Affordable extra transaction fee is already configured!\n" && exit 0 + echomult "\nStarting the reconfiguration...\n" - echomult "Using affordable extra transaction fee $extra_txn_fee XAH Drops." + # Stop the message board service. + echomult "Stopping the message board..." + sudo -u "$MB_XRPL_USER" XDG_RUNTIME_DIR="$mb_user_runtime_dir" systemctl --user stop $MB_XRPL_SERVICE - update_mb=1 + # Stop the sashimono service. + if [ $update_sashi == 1 ]; then + echomult "Stopping the sashimono..." + systemctl stop $SASHIMONO_SERVICE - else - echomult "Invalid arguments.\n Usage: evernode config [resources|leaseamt|rippled|email|instance|extrafee] [arguments]\n" && exit 1 - fi + ! reconfig_sashi && has_error=1 - local mb_user_id=$(id -u "$MB_XRPL_USER") - local mb_user_runtime_dir="/run/user/$mb_user_id" - local has_error=0 + echomult "Starting the sashimono..." + systemctl start $SASHIMONO_SERVICE + fi - echomult "\nStarting the reconfiguration...\n" + if [ $has_error == 0 ] && [ $update_mb == 1 ]; then + ! reconfig_mb && has_error=1 + fi - # Stop the message board service. - echomult "Stopping the message board..." - sudo -u "$MB_XRPL_USER" XDG_RUNTIME_DIR="$mb_user_runtime_dir" systemctl --user stop $MB_XRPL_SERVICE + echomult "Starting the message board..." + sudo -u "$MB_XRPL_USER" XDG_RUNTIME_DIR="$mb_user_runtime_dir" systemctl --user start $MB_XRPL_SERVICE - # Stop the sashimono service. - if [ $update_sashi == 1 ] ; then - echomult "Stopping the sashimono..." - systemctl stop $SASHIMONO_SERVICE + [ $has_error == 1 ] && echomult "\nChanging the configuration exited with an error.\n" && exit 1 - ! reconfig_sashi && has_error=1 + echomult "\nSuccessfully changed the configuration!\n" + } - echomult "Starting the sashimono..." - systemctl start $SASHIMONO_SERVICE - fi + function delete_instance() { + [ "$EUID" -ne 0 ] && echo "Please run with root privileges (sudo)." && exit 1 - if [ $has_error == 0 ] && [ $update_mb == 1 ] ; then - ! reconfig_mb && has_error=1 - fi + # Restart the message board to update the instance count + local mb_user_id=$(id -u "$MB_XRPL_USER") + local mb_user_runtime_dir="/run/user/$mb_user_id" - echomult "Starting the message board..." - sudo -u "$MB_XRPL_USER" XDG_RUNTIME_DIR="$mb_user_runtime_dir" systemctl --user start $MB_XRPL_SERVICE + echomult "Stopping the message board..." + sudo -u "$MB_XRPL_USER" XDG_RUNTIME_DIR="$mb_user_runtime_dir" systemctl --user stop $MB_XRPL_SERVICE - [ $has_error == 1 ] && echomult "\nChanging the configuration exited with an error.\n" && exit 1 + local has_error=0 + instance_name=$1 + echo "Deleting instance $instance_name" + ! sudo -u $MB_XRPL_USER MB_DATA_DIR=$MB_XRPL_DATA node $MB_XRPL_BIN delete $instance_name && + echo "There was an error in deleting the instance." && has_error=1 - echomult "\nSuccessfully changed the configuration!\n" -} + echomult "Starting the message board..." + sudo -u "$MB_XRPL_USER" XDG_RUNTIME_DIR="$mb_user_runtime_dir" systemctl --user start $MB_XRPL_SERVICE -function delete_instance() -{ - [ "$EUID" -ne 0 ] && echo "Please run with root privileges (sudo)." && exit 1 + [ $has_error == 0 ] && echo "Instance deletion completed." + } - # Restart the message board to update the instance count - local mb_user_id=$(id -u "$MB_XRPL_USER") - local mb_user_runtime_dir="/run/user/$mb_user_id" + # Begin setup execution flow -------------------- - echomult "Stopping the message board..." - sudo -u "$MB_XRPL_USER" XDG_RUNTIME_DIR="$mb_user_runtime_dir" systemctl --user stop $MB_XRPL_SERVICE + if [ "$mode" == "install" ]; then - local has_error=0 - instance_name=$1 - echo "Deleting instance $instance_name" - ! sudo -u $MB_XRPL_USER MB_DATA_DIR=$MB_XRPL_DATA node $MB_XRPL_BIN delete $instance_name && - echo "There was an error in deleting the instance." && has_error=1 + # Check if message board config exists. + # This means installation has passed through configuration. + if [ ! -f "$MB_XRPL_DATA/mb-xrpl.cfg" ]; then - echomult "Starting the message board..." - sudo -u "$MB_XRPL_USER" XDG_RUNTIME_DIR="$mb_user_runtime_dir" systemctl --user start $MB_XRPL_SERVICE - - [ $has_error == 0 ] && echo "Instance deletion completed." -} - -# Begin setup execution flow -------------------- - -if [ "$mode" == "install" ]; then - - ! confirm "This will install Sashimono, Evernode's contract instance management software, + ! confirm "This will install Sashimono, Evernode's contract instance management software, and register your system as an $evernode host. \nMake sure your system does not currently contain any other workloads important to you since we will be making modifications to your system configuration. \n\nContinue?" && exit 1 - check_sys_req - check_prereq + check_sys_req + check_prereq - - # Display licence file and ask for concent. - printf "\n***********************************************************************************************************************\n\n" - echomult "EVERNODE SOFTWARE LICENCE AGREEMENT" - echomult "\nBy using this EVERNODE CLI Tool, you agree to be bound by the terms and conditions of the EVERNODE SOFTWARE LICENCE. + # Display licence file and ask for concent. + printf "\n***********************************************************************************************************************\n\n" + echomult "EVERNODE SOFTWARE LICENCE AGREEMENT" + echomult "\nBy using this EVERNODE CLI Tool, you agree to be bound by the terms and conditions of the EVERNODE SOFTWARE LICENCE. \nFor full details, please refer to the licence document available at: \n$licence_url" - printf "\n\n***********************************************************************************************************************\n" - ! confirm "\nDo you accept the terms of the licence agreement?" && exit 1 + printf "\n\n***********************************************************************************************************************\n" + ! confirm "\nDo you accept the terms of the licence agreement?" && exit 1 - ! confirm "\nAre you performing a fresh Evernode installation? - \nNOTE: Pressing 'n' implies that you are in the process of transferring from a previous installation in $NETWORK." && operation="re-register" + set_environment_configs - set_environment_configs + init_setup_helpers - init_setup_helpers + set_rippled_server + echo -e "Using Rippled server '$rippled_server'.\n" - if [ "$NO_MB" == "" ]; then - set_rippled_server - echo -e "Using Rippled server '$rippled_server'.\n" - fi + set_email_address + echo -e "Using the contact email address '$email_address'.\n" - set_email_address - echo -e "Using the contact email address '$email_address'.\n" + # TODO - CHECKPOINT - 01 + set_inet_addr + echo -e "Using '$inetaddr' as host internet address.\n" - # TODO - CHECKPOINT - 01 - set_inet_addr - echo -e "Using '$inetaddr' as host internet address.\n" + set_country_code + echo -e "Using '$countrycode' as country code.\n" - set_country_code - echo -e "Using '$countrycode' as country code.\n" + set_ipv6_subnet + [ "$ipv6_subnet" != "-" ] && [ "$ipv6_net_interface" != "-" ] && echo -e "Using $ipv6_subnet IPv6 subnet on $ipv6_net_interface for contract instances.\n" - set_ipv6_subnet - [ "$ipv6_subnet" != "-" ] && [ "$ipv6_net_interface" != "-" ] && echo -e "Using $ipv6_subnet IPv6 subnet on $ipv6_net_interface for contract instances.\n" + set_cgrules_svc + echo -e "Using '$cgrulesengd_service' as cgroups rules engine service.\n" - set_cgrules_svc - echo -e "Using '$cgrulesengd_service' as cgroups rules engine service.\n" + set_instance_alloc + echo -e "Using allocation $(GB $alloc_ramKB) memory, $(GB $alloc_swapKB) Swap, $(GB $alloc_diskKB) disk space, distributed among $alloc_instcount contract instances.\n" - set_instance_alloc - echo -e "Using allocation $(GB $alloc_ramKB) memory, $(GB $alloc_swapKB) Swap, $(GB $alloc_diskKB) disk space, distributed among $alloc_instcount contract instances.\n" + set_init_ports + echo -e "Using peer port range $init_peer_port-$((init_peer_port + alloc_instcount)) and user port range $init_user_port-$((init_user_port + alloc_instcount))).\n" - set_init_ports - echo -e "Using peer port range $init_peer_port-$((init_peer_port + alloc_instcount)) and user port range $init_user_port-$((init_user_port + alloc_instcount))).\n" + set_lease_amount + echo -e "Lease amount set as $lease_amount EVRs per Moment.\n" - if [ "$NO_MB" == "" ]; then - set_lease_amount - echo -e "Lease amount set as $lease_amount EVRs per Moment.\n" + set_extra_fee - set_extra_fee + fi # TODO - CHECKPOINT - 02 set_host_xrpl_account $operation echo -e "\nAccount setup is complete." - fi - set_auto_update - if [ "$enable_auto_update" = true ]; then - echo -e "Auto updater will be enabled." - else - echo -e "Auto updater will be disabled." - fi + $interactive && ! confirm "\n\nSetup will now begin the installation. Continue?" && exit 1 - $interactive && ! confirm "\n\nSetup will now begin the installation. Continue?" && exit 1 + # TODO - CHECKPOINT - 03 + echo "Starting installation..." + install_evernode 0 - # TODO - CHECKPOINT - 03 - echo "Starting installation..." - install_evernode 0 + rm -r $setup_helper_dir >/dev/null 2>&1 - rm -r $setup_helper_dir >/dev/null 2>&1 - - echomult "Installation successful! Installation log can be found at $logfile + echomult "Installation successful! Installation log can be found at $logfile \n\nYour system is now registered on $evernode. You can check your system status with 'evernode status' command." -elif [ "$mode" == "uninstall" ]; then + elif [ "$mode" == "uninstall" ]; then - echomult "\nNOTE: By continuing with this, you will not LOSE the SECRET; it remains within the specified path. + echomult "\nNOTE: By continuing with this, you will not LOSE the SECRET; it remains within the specified path. \nThe secret path can be found inside the configuration stored at '$MB_XRPL_DATA/mb-xrpl.cfg'." - ! confirm "\nAre you sure you want to uninstall $evernode?" && exit 1 - - - # Check contract condtion. - check_exisiting_contracts 0 - - # Perform Evernode uninstall - uninstall_evernode 0 - echo "Uninstallation complete!" - -elif [ "$mode" == "transfer" ]; then - # If evernode is not installed download setup helpers and call for transfer. - if $installed ; then - - if ! $interactive ; then - transferee_address=${3} # Address of the transferee. - else - - ! confirm "\nThis will uninstall and deregister this host from $evernode - while allowing you to transfer the registration to a preferred transferee. - \n\nAre you sure you want to transfer $evernode registration from this host?" && exit 1 - - echomult "\nNOTE: By continuing with this, you will not LOSE the SECRET; it remains within the specified path. - \nThe secret path can be found inside the configuration stored at '$MB_XRPL_DATA/mb-xrpl.cfg'." - - ! confirm "\nAre you sure you want to continue?" && exit 1 - - fi - - # Set transferee based on the user input. - set_transferee_address + ! confirm "\nAre you sure you want to uninstall $evernode?" && exit 1 # Check contract condtion. check_exisiting_contracts 0 - # Initiate transferring. - init_evernode_transfer + #Perform Evernode uninstall + uninstall_evernode 0 + echo "Uninstallation complete!" - # Execute oftware uninstallation (Force uninstall on quiet mode). - $interactive && uninstall_evernode 0 || uninstall_evernode 0 -f + elif [ "$mode" == "transfer" ]; then + # If evernode is not installed download setup helpers and call for transfer. + if $installed; then - else - if ! $interactive ; then - xrpl_account_address=${3} # XRPL account address. - xrpl_account_secret=$(<"${4}") # XRPL account secret based on the provided path. - transferee_address=${5} # Address of the transferee. - rippled_server=${6} # Rippled server URL - fi + if ! $interactive; then + transferee_address=${3} # Address of the transferee. + else - check_common_prereq + ! confirm "\nThis will uninstall and deregister this host from $evernode + while allowing you to transfer the registration to a preferred transferee. + \n\nAre you sure you want to transfer $evernode registration from this host?" && exit 1 - set_environment_configs + echomult "\nNOTE: By continuing with this, you will not LOSE the SECRET; it remains within the specified path. + \nThe secret path can be found inside the configuration stored at '$MB_XRPL_DATA/mb-xrpl.cfg'." - init_setup_helpers + ! confirm "\nAre you sure you want to continue?" && exit 1 - # Set rippled server based on the user input. - set_rippled_server - echo -e "Using Rippled server '$rippled_server'.\n" + fi - # Set host account based on the user input. - set_host_xrpl_account "transfer" + # Set transferee based on the user input. + set_transferee_address - # Set transferee based on the user input. - set_transferee_address + # Check contract condtion. + check_exisiting_contracts 0 - $interactive && ! confirm "\nThis will deregister $xrpl_account_address from $evernode + # Initiate transferring. + init_evernode_transfer + + # Execute oftware uninstallation (Force uninstall on quiet mode). + $interactive && uninstall_evernode 0 || uninstall_evernode 0 -f + + else + if ! $interactive; then + xrpl_account_address=${3} # XRPL account address. + xrpl_account_secret=$(<"${4}") # XRPL account secret based on the provided path. + transferee_address=${5} # Address of the transferee. + rippled_server=${6} # Rippled server URL + fi + + check_common_prereq + + set_environment_configs + + init_setup_helpers + + # Set rippled server based on the user input. + set_rippled_server + echo -e "Using Rippled server '$rippled_server'.\n" + + # Set host account based on the user input. + set_host_xrpl_account "transfer" + + # Set transferee based on the user input. + set_transferee_address + + $interactive && ! confirm "\nThis will deregister $xrpl_account_address from $evernode while allowing you to transfer the registration to $([ -z $transferee_address ] && echo "same account" || echo "$transferee_address"). \n\nAre you sure you want to transfer $evernode registration?" && exit 1 - config_json_path="/tmp/evernode-setup-helpers/configuration.json" - export EVERNODE_GOVERNOR_ADDRESS=${OVERRIDE_EVERNODE_GOVERNOR_ADDRESS:-$(jq -r ".$NETWORK.governorAddress" $config_json_path)} + config_json_path="/tmp/evernode-setup-helpers/configuration.json" + export EVERNODE_GOVERNOR_ADDRESS=${OVERRIDE_EVERNODE_GOVERNOR_ADDRESS:-$(jq -r ".$NETWORK.governorAddress" $config_json_path)} - # Execute transfer from js helper. - exec_jshelper transfer $rippled_server $EVERNODE_GOVERNOR_ADDRESS $xrpl_account_address $xrpl_account_secret $transferee_address + # Execute transfer from js helper. + exec_jshelper transfer $rippled_server $EVERNODE_GOVERNOR_ADDRESS $xrpl_account_address $xrpl_account_secret $transferee_address - rm -r $setup_helper_dir >/dev/null 2>&1 - fi + rm -r $setup_helper_dir >/dev/null 2>&1 + fi - echo "Transfer process was sucessfully initiated. You can now install and register $evernode using the account $transferee_address." + echo "Transfer process was sucessfully initiated. You can now install and register $evernode using the account $transferee_address." -elif [ "$mode" == "status" ]; then - reg_info + elif [ "$mode" == "status" ]; then + reg_info -elif [ "$mode" == "list" ]; then - sashi list + elif [ "$mode" == "list" ]; then + sashi list -elif [ "$mode" == "update" ]; then - update_evernode + elif [ "$mode" == "update" ]; then + update_evernode -elif [ "$mode" == "log" ]; then - create_log + elif [ "$mode" == "log" ]; then + create_log -elif [ "$mode" == "applyssl" ]; then - apply_ssl $2 $3 $4 + elif [ "$mode" == "applyssl" ]; then + apply_ssl $2 $3 $4 -elif [ "$mode" == "config" ]; then - config $2 $3 $4 $5 $6 + elif [ "$mode" == "config" ]; then + config $2 $3 $4 $5 $6 -elif [ "$mode" == "delete" ]; then - [ -z "$2" ] && echomult "A contract instance name must be specified (see 'evernode list').\n Usage: evernode delete " && exit 1 + elif [ "$mode" == "delete" ]; then + [ -z "$2" ] && echomult "A contract instance name must be specified (see 'evernode list').\n Usage: evernode delete " && exit 1 - delete_instance "$2" + delete_instance "$2" -elif [ "$mode" == "governance" ]; then - [[ "$2" == "" || "$2" == "help" ]] && echomult "Governance management tool + elif [ "$mode" == "governance" ]; then + [[ "$2" == "" || "$2" == "help" ]] && echomult "Governance management tool \nSupported commands: \npropose [hashFile] [shortName] - Propose new governance candidate. \nwithdraw [candidateId] - Withdraw proposed governance candidate. @@ -1860,43 +1885,43 @@ elif [ "$mode" == "governance" ]; then \nstatus - Get governance info of this host. \nreport [dudHostAddress] - Report a dud host. \nhelp - Print help." && exit 0 - ! MB_DATA_DIR=$MB_XRPL_DATA node $MB_XRPL_BIN ${*:1} && exit 1 + ! MB_DATA_DIR=$MB_XRPL_DATA node $MB_XRPL_BIN ${*:1} && exit 1 -elif [ "$mode" == "auto-update" ]; then - if [ "$2" == "enable" ]; then - confirm "Are you sure you want to subscribe for auto-updates?\nNOTE: The auto-update service is offered subject to the terms set out in the Evernode Software Licence." && enable_evernode_auto_updater && exit 0 - elif [ "$2" == "disable" ]; then - remove_evernode_auto_updater && exit 0 - else - echomult "$evernode auto update + elif [ "$mode" == "auto-update" ]; then + if [ "$2" == "enable" ]; then + confirm "Are you sure you want to subscribe for auto-updates?\nNOTE: The auto-update service is offered subject to the terms set out in the Evernode Software Licence." && enable_evernode_auto_updater && exit 0 + elif [ "$2" == "disable" ]; then + remove_evernode_auto_updater && exit 0 + else + echomult "$evernode auto update \nSupported commands: \nenable - Enable $evernode auto updater service. \ndisable - Disable $evernode auto updater service." && exit 1 - fi - -elif [ "$mode" == "regkey" ]; then - if [ "$2" == "set" ]; then - if [ -z "$3" ]; then - echo "Regular key to be set must be provided." && exit 1 - elif [[ ! "$3" =~ ^[[:alnum:]]{24,34}$ ]]; then - echo "Regular key is invalid." && exit 1 fi - set_regular_key $3 - exit 0 - elif [ "$2" == "delete" ]; then - set_regular_key - exit 0 - else - echomult "Regular key management tool + + elif [ "$mode" == "regkey" ]; then + if [ "$2" == "set" ]; then + if [ -z "$3" ]; then + echo "Regular key to be set must be provided." && exit 1 + elif [[ ! "$3" =~ ^[[:alnum:]]{24,34}$ ]]; then + echo "Regular key is invalid." && exit 1 + fi + set_regular_key $3 + exit 0 + elif [ "$2" == "delete" ]; then + set_regular_key + exit 0 + else + echomult "Regular key management tool \nSupported commands: \nset [regularKey] - Assign or update the regular key. \ndelete - Delete the regular key" && exit 1 + fi fi -fi -[ "$mode" != "uninstall" ] && check_installer_pending_finish + [ "$mode" != "uninstall" ] && check_installer_pending_finish -exit 0 + exit 0 -# surrounding braces are needed make the whole script to be buffered on client before execution. + # surrounding braces are needed make the whole script to be buffered on client before execution. } diff --git a/mb-xrpl/app.js b/mb-xrpl/app.js index d9f9083..e8a7f68 100644 --- a/mb-xrpl/app.js +++ b/mb-xrpl/app.js @@ -35,6 +35,15 @@ async function main() { await new Setup().register(process.argv[3], parseInt(process.argv[4]), parseInt(process.argv[5]), parseInt(process.argv[6]), parseInt(process.argv[7]), parseInt(process.argv[8]), process.argv[9], parseInt(process.argv[10]), parseInt(process.argv[11]), process.argv[12], process.argv[13]); } + else if (process.argv.length >= 3 && process.argv[2] === 'mint-leases') { + await new Setup().mintLeases(process.argv[3]); + } + else if (process.argv.length >= 2 && process.argv[2] === 'offer-leases') { + await new Setup().offerLeases(); + } + else if (process.argv.length >= 2 && process.argv[2] === 'burn-leases') { + await new Setup().burnLeases(); + } else if (process.argv.length >= 3 && process.argv[2] === 'transfer') { (process.argv[3]) ? await new Setup().transfer(process.argv[3]) : await new Setup().transfer(); } diff --git a/mb-xrpl/lib/setup.js b/mb-xrpl/lib/setup.js index c081dc7..57d8f17 100644 --- a/mb-xrpl/lib/setup.js +++ b/mb-xrpl/lib/setup.js @@ -1,5 +1,4 @@ -const https = require('https'); const { appenv } = require('./appenv'); const evernode = require('evernode-js-client'); const fs = require('fs'); @@ -125,13 +124,6 @@ class Setup { await hostClient.register(countryCode, cpuMicroSec, Math.floor((ramKb + swapKb) / 1000), Math.floor(diskKb / 1000), totalInstanceCount, cpuModelFormatted.substring(0, 40), cpuCount, cpuSpeed, description.replaceAll('_', ' '), emailAddress, { retryOptions: { maxRetryAttempts: MAX_TX_RETRY_ATTEMPTS, feeUplift: Math.floor(acc.affordableExtraFee / MAX_TX_RETRY_ATTEMPTS) } }); - // Create lease offers. - console.log("Creating lease offers for instance slots..."); - for (let i = 0; i < totalInstanceCount; i++) { - await hostClient.offerLease(i, acc.leaseAmount, appenv.TOS_HASH, config?.networking?.ipv6?.subnet ? UtilHelper.generateIPV6Address(config.networking.ipv6.subnet, i) : null, { retryOptions: { maxRetryAttempts: MAX_TX_RETRY_ATTEMPTS, feeUplift: Math.floor(acc.affordableExtraFee / MAX_TX_RETRY_ATTEMPTS) } }); - console.log(`Created lease offer ${i + 1} of ${totalInstanceCount}.`); - } - break; } catch (err) { @@ -148,6 +140,144 @@ class Setup { await hostClient.disconnect(); } + async mintLeases(totalInstanceCount) { + const config = this.#getConfig(); + const acc = config.xrpl; + await setEvernodeDefaults(acc.network, acc.governorAddress, acc.rippledServer); + + const hostClient = new evernode.HostClient(acc.address, acc.secret); + await hostClient.connect(); + + // Update the Defaults with "xrplApi" of the client. + evernode.Defaults.set({ + xrplApi: hostClient.xrplApi + }); + + try { + const leases = await hostClient.getLeases(); + + // Terminate if existing leases are inconsistent with current. + let lastIndex = 0; + if (leases.length) { + for (const l of leases) { + if (l.Amount && l.Amount.value !== acc.leaseAmount) { + throw 'LEASE_ERR: Lease amount is inconsistent with existing.'; + } + const tokenInfo = evernode.UtilHelpers.decodeLeaseTokenUri(l.URI); + if (tokenInfo.leaseAmount !== acc.leaseAmount) { + throw 'LEASE_ERR: Lease amount is inconsistent with existing.'; + } + const leaseIndex = tokenInfo.leaseIndex; + const outboundIP = tokenInfo.outboundIP; + + if ((outboundIP && !config?.networking?.ipv6?.subnet) || (!outboundIP && config?.networking?.ipv6?.subnet)) { + throw 'LEASE_ERR: Outbound IP is inconsistent with existing.'; + } + else if (outboundIP && config?.networking?.ipv6?.subnet) { + if (!UtilHelper.isSameIPV6Subnet(outboundIP, config?.networking?.ipv6?.subnet)) { + throw 'LEASE_ERR: Outbound IP is inconsistent with existing.'; + } + } + + if (leaseIndex > lastIndex) { + lastIndex = leaseIndex; + } + } + } + + if (totalInstanceCount >= leases.length) { + // Create leases. + console.log("Minting leases for instance slots..."); + for (let i = (leases.length > 0 ? (lastIndex + 1) : 0); i < totalInstanceCount; i++) { + await hostClient.mintLease(i, acc.leaseAmount, appenv.TOS_HASH, config?.networking?.ipv6?.subnet ? UtilHelper.generateIPV6Address(config.networking.ipv6.subnet, i) : null, { retryOptions: { maxRetryAttempts: MAX_TX_RETRY_ATTEMPTS, feeUplift: Math.floor(acc.affordableExtraFee / MAX_TX_RETRY_ATTEMPTS) } }); + console.log(`Minted lease ${i + 1} of ${totalInstanceCount}.`); + } + } + else { + // Burn leases. + console.log("Burning previous leases..."); + for (let i = totalInstanceCount; i < leases.length; i++) { + await hostClient.expireLease(leases[i].index, { retryOptions: { maxRetryAttempts: MAX_TX_RETRY_ATTEMPTS, feeUplift: Math.floor(acc.affordableExtraFee / MAX_TX_RETRY_ATTEMPTS) } }); + console.log(`Burned lease ${i + 1} of ${leases.length}.`); + } + } + await hostClient.disconnect(); + } + catch (e) { + await hostClient.disconnect(); + throw e; + } + } + + async offerLeases() { + const config = this.#getConfig(); + const acc = config.xrpl; + await setEvernodeDefaults(acc.network, acc.governorAddress, acc.rippledServer); + + const hostClient = new evernode.HostClient(acc.address, acc.secret); + await hostClient.connect(); + + // Update the Defaults with "xrplApi" of the client. + evernode.Defaults.set({ + xrplApi: hostClient.xrplApi + }); + + try { + const unoffered = await hostClient.getUnofferedLeases(); + + if (unoffered.length > 0) { + // Create lease offers. + console.log("Creating lease offers for instance slots..."); + let i = 0; + for (let t of unoffered) { + const uriInfo = evernode.UtilHelpers.decodeLeaseTokenUri(uriToken.URI); + if (uriInfo.leaseAmount == acc.leaseAmount) { + await hostClient.offerMintedLease(t.index, acc.leaseAmount, { retryOptions: { maxRetryAttempts: MAX_TX_RETRY_ATTEMPTS, feeUplift: Math.floor(acc.affordableExtraFee / MAX_TX_RETRY_ATTEMPTS) } }); + console.log(`Created lease offer ${i + 1} of ${unoffered.length}.`); + } + else { + throw 'LEASE_ERR: Lease amounts are inconsistent.'; + } + i++; + } + } + else { + throw 'LEASE_ERR: No unoffered leases.'; + } + + await hostClient.disconnect(); + } + catch (e) { + await hostClient.disconnect(); + throw e; + } + + } + + async burnLeases() { + const config = this.#getConfig(); + const acc = config.xrpl; + await setEvernodeDefaults(acc.network, acc.governorAddress, acc.rippledServer); + + const hostClient = new evernode.HostClient(acc.address, acc.secret); + await hostClient.connect(); + + // Update the Defaults with "xrplApi" of the client. + evernode.Defaults.set({ + xrplApi: hostClient.xrplApi + }); + + try { + await this.burnMintedURITokens(hostClient, { retryOptions: { maxRetryAttempts: MAX_TX_RETRY_ATTEMPTS, feeUplift: Math.floor(acc.affordableExtraFee / MAX_TX_RETRY_ATTEMPTS) } }); + await hostClient.disconnect(); + } + catch (e) { + await hostClient.disconnect(); + throw e; + } + + } + async deregister(error = null) { console.log("Deregistering host..."); const acc = this.#getConfig().xrpl; diff --git a/mb-xrpl/lib/util-helper.js b/mb-xrpl/lib/util-helper.js index c53d6da..6a82a35 100644 --- a/mb-xrpl/lib/util-helper.js +++ b/mb-xrpl/lib/util-helper.js @@ -18,6 +18,27 @@ class UtilHelper { return null; } + + static isSameIPV6Subnet(address1, address2) { + const [ip1, ip1PrefixLen] = address1.split('/'); + const [ip2, ip2PrefixLen] = address2.split('/'); + + if (!(ip1 && ip2 && ip1PrefixLen && ip2PrefixLen && !isNaN(ip1PrefixLen) && !isNaN(ip2PrefixLen))) + return false; + + try { + // This will return the normalized abbreviated subnet CIDR notation. + const ip1Cidr = ip6addr.createCIDR(ip1, parseInt(ip1PrefixLen)); + const ip2Cidr = ip6addr.createCIDR(ip2, parseInt(ip2PrefixLen)); + + return (ip1Cidr === ip2Cidr) + } + catch { + // Silent catch so that we don't log exceptions to console. + // This will be treated as ip validation failure. + return false; + } + } } module.exports = {