Install Script

Script to perform an all-in-one installation, configuration and startup of FIO

The following script encapsulates the workflow and commands from FIO Package Install and FIO Nodeos Replay. It is offered as a means to quickly build and run a node. As such, details on how to connect, and register as a FIO Chain node are not provided. In addition assumptions have been made as to the local environment as well as FIO package URLs, package versioning and authenticity, etc., which may have to be verified and/or updated for the following script to function properly. Please refer to the aforementioned pages for detailed information.

🚧

Destructive

The following script must be run as root/using sudo. Note that the use of 'sudo' is for Advanced users!

#!/usr/bin/env bash

echo
if [[ "$EUID" -ne 0 ]]; then
  echo "ERROR: Script must be run as root! Use sudo command as follows; sudo ./<script name>"
  echo
  exit 1
fi

# Utility functions
function pause() {
  echo
  read -s -n 1 -p "Press any key to continue (CTRL-c to exit)..."
  echo
  echo
}

function getAndExtract() {
  uri=${1}
  rsrc=${2}

  exitStatus=0

  echo
  echo "Cleaning target directory '/var/lib/fio'..."
  rm -rf /var/lib/fio/data /var/lib/fio/history /var/lib/fio/history_index
  echo
  echo -n "Downloading archive..."
  wget -bq --tries 1 --timeout=10 ${uri}/${rsrc}
  echo
  max=1200
  for ((i=1; i<=max; i++)); do
    echo -n '.'
    sleep 1s
    if [[ $(pgrep -f latest) -eq 0 ]]; then
      break
    fi
  done
  echo
  echo
  if [[ ! -e ${rsrc} ]]; then
    exitStatus=99
    echo "ERROR: Something went wrong downloading archive ${rsrc} from ${uri}!"
    pause
  else
    echo Download complete!
    echo
    echo "Exracting archives to target directory (silently)..."
    tar -xS -I'pixz' -C /var/lib/fio -f ${rsrc}
    if [[ $? -ne 0 ]]; then
      exitStatus=98
      echo
      echo "ERROR: Something went wrong extracting contents of ${rsrc}! Inspect archive and fix any errors."
      pause
    else
      echo
      echo "Archive successfully extracted to '/var/lib/fio'"
      mkdir -p /var/lib/fio/history /var/lib/fio/history_index
      chown -R fio:fio /var/lib/fio
      chmod -R 0755 /var/lib/fio

      echo
      read -N 1 -p "Remove downloaded archive (y/N)? " answer
      if [[ "${answer,,}" == "y" ]]; then
        rm -f ${rsrc}
      fi
    fi
  fi

  return ${exitStatus}
}

function do_configHelp() {
  local type="${1}"

  echo
  echo "Review configuration settings for your install"
  echo "- producer-name"
  echo "- endpoints and ports"
  echo "- history settings"
  echo
  if [[ ${type} == "v1" || ${type} == "all" ]]; then
    # Add/Uncomment the following configuration in the nodeos config.ini for history processing
    echo
    echo "Add/uncomment the following configuration in the fio-nodeos config.ini located"
    echo "in /etc/fio/nodeos"
    echo
    echo plugin = eosio::history_plugin
    echo plugin = eosio::history_api_plugin
    echo filter-on = *
    echo filter-out = eosio:onblock:
    echo history-per-account = 9223372036854775807
    echo history-index-state-db-size-mb = 1000000
    echo history-state-db-size-mb = 4000000
  fi
  if [[ ${type} == "state" || ${type} == "all" ]]; then
    echo
    echo "Add/uncomment the following configuration in the fio-nodeos config.ini located"
    echo "in /etc/fio/nodeos"
    echo
    echo plugin = eosio::state_history_plugin
    echo state-history-dir = state-history
    echo trace-history = true
    echo chain-state-history = true
    echo state-history-endpoint = 0.0.0.0:8080
  fi
}

function do_configure() {
  echo
  read -N 1 -p "Edit the FIO Nodeos configuration (y/N)? " answer
  if [[ "${answer,,}" == "y" ]]; then
    vi /etc/fio/nodeos/config.ini
  fi
  echo && echo
  read -N 1 -p "Edit the FIO Nodeos exec (for state history only) (y/N)? " answer
  if [[ "${answer,,}" == "y" ]]; then
    vi /usr/local/bin/fio-nodeos-run
  fi
  echo && echo

}

function do_installuninstall() {
  local PS3="Select Action: "

  local items=("Install FIO" "Uninstall FIO" )

  select item in "${items[@]}" "Return to Main Menu"
  do
    echo
    echo -n "You've chosen to "
    case $REPLY in
      1) echo "$item"; do_install; break;;
      2) echo "$item"; do_uninstall; break;;
      $((${#items[@]}+1))) echo "Return to Main Menu!"; echo; break 2;;
      *) echo "Ooops - unknown choice $REPLY"; break;
    esac
  done
}

function do_uninstall() {
  echo
  echo "Confirm complete removal of the fioprotocol package and all related artifacts..."
  pause

  do_stop true

  # Remove installed package
  echo
  echo "Removing fioprotocol package, including binaries, from system..."
  pause
  apt remove fioprotocol

  # Remove leftover artifacts from fio package install
  echo
  echo "Removing fioprotocol binaries from system..."
  rm -f /usr/local/bin/cleos
  rm -f /usr/local/bin/fio-cleos
  rm -f /usr/local/bin/clio
  rm -f /usr/local/bin/nodeos
  rm -f /usr/local/bin/fio-nodeos
  rm -f /usr/local/bin/fio-wallet

  # ********************************** WARNING ***********************************
  # The following commands remove runtime data that is critical for node execution
  # Only proceed if cleanup of an outdated install is desired
  # ******************************************************************************
  # Remove configuration, blocks log, history, state
  echo
  echo "Removing fioprotocol artifacts including configuration, blocks, state, and history, from system..."
  pause
  rm -rf /etc/fio
  rm -rf /var/lib/fio
  rm -rf /var/log/fio

  # Remove the fio user
  # This step is useful if not re-installing/upgrading the fio package
  echo
  echo "Removing fioprotocol user from system..."
  pause
  #deluser --system --remove-home --group fio fio
  deluser --force fio &>/dev/null
  echo
}

function do_install() {
  echo
  read -N 1 -p "Is this an install of TestNet (y/N)? " answer
  if [[ "${answer,,}" == "y" ]]; then
    type="testnet"
  else
    echo
    echo
    read -N 1 -p "Is this an install of MainNet (y/N)? " answer
    if [[ "${answer,,}" == "y" ]]; then
      type="mainnet"
    fi
  fi
  if [[ -z "${type}" ]]; then
    echo
    echo
    echo "Neither install type of TestNet nor MainNet was selected! Aborting install..."
    echo
    return
  fi
  echo
  echo
  read -N 1 -p "You have chosen a '${type}' install. Correct (y/N)? " answer
  if [[ "${answer,,}" != "y" ]]; then
    echo
    echo
    echo Aborting install!
    echo
    return
  fi

  # Determine OS
  . /etc/os-release
  if [[ ${VERSION_ID} != '18.04' && ${VERSION_ID} != '20.04' ]]; then
    echo
    echo "Only Ubuntu 18.04 and 20.04 are supported at this time. Exiting..."
    echo && return
  fi

  echo
  echo
  echo "Checking FIO Package dependencies..."
  dpkg-query -W pixz &>/dev/null
  if [[ $? -ne 0 ]]; then
    echo
    echo "Package dependency check failed! pixz is not installed and must be before proceeding."
    echo "Install using the commands;"
    echo "  sudo apt install pixz"
    echo
    echo "Note that you may need to run 'sudo apt update && sudo apt upgrade -y' first"
    echo
    read -N 1 -p "Do you wish to install 'pixz' now (y/N)? " answer
    if [[ "${answer,,}" != "y" ]]; then
	  echo && apt update && apt upgrade -y && echo
	  apt install pixz
    else
	  echo & return
	fi
  fi
  if [[ ${VERSION_ID} == '20.04' ]]; then
    dpkg-query -W libicu60\* &>/dev/null
    if [[ $? -ne 0 ]]; then
      echo
      echo "Package dependency check failed; libicu60 is not installed!"
      echo
      echo "Get and install libicu60 by;"
	  echo "  Original URL: wget http://archive.ubuntu.com/ubuntu/pool/main/i/icu/libicu60_60.2-3ubuntu3_amd64.deb"
      echo "  FIO S3 URL: wget https://fioprotocol.s3.us-east-1.amazonaws.com/build-tools/libicu60_60.2-3ubuntu3_amd64.deb"
	  echo
      echo "  sudo apt install ./libicu60_60.2-3ubuntu3_amd64.deb"
      echo
      echo "Note that you may need to run 'sudo apt upgrade && sudo apt update -y' first"
	  echo
      read -N 1 -p "Do you wish to install 'libicu60' now (y/N)? " answer
      if [[ "${answer,,}" != "y" ]]; then
	    echo && apt update && echo
	    apt install ./libicu60_60.2-3ubuntu3_amd64.deb
      else
        echo && return
      fi
	fi
  fi
  echo "Package dependency check complete."

  # FIO package download examples
  # Release candidates and Release versions
  # https://github.com/fioprotocol/fio/releases/download/v3.5.1/fioprotocol-3.5.1-rc1-ubuntu-20.04-amd64.deb
  # https://github.com/fioprotocol/fio/releases/download/v3.5.1/fioprotocol-3.5.1-ubuntu-20.04-amd64.deb

  # https://github.com/fioprotocol/fio/releases/download/v3.5.1/fioprotocol-3.5.1-rc1-ubuntu-22.04-amd64.deb
  # https://github.com/fioprotocol/fio/releases/download/v3.5.1/fioprotocol-3.5.1-ubuntu-22.04-amd64.deb

  # Note: Release Name only is used in download action
  # GitHub
  # release=3.5.0-rc1
  # release=3.5.1

  release=""
  echo
  echo "The release package version is required and must match a release package located at"
  echo " - https://github.com/fioprotocol/fio/releases"
  echo "Examples include:"
  echo " - 3.5.0-rc1"
  echo " - 3.5.1"
  echo
  read -p "Enter the release package version: " answer
  if [[ "${answer,,}" != "" ]]; then
    release="${answer}"
    echo
    read -N 1 -p "You input '${release}'. Correct (y/N)? " answer
    if [[ "${answer,,}" != "y" ]]; then
      echo
      echo
      echo Aborting install!
      echo
      return
    fi
  else
    echo
    echo
    echo "No release package name entered, Aborting install!"
    echo
    return
  fi

  # Release path options
  # GitHub
  path="fio/releases/download/v${release}"

  # Package filename
  filename="fioprotocol-${release}-ubuntu-${VERSION_ID}-amd64.deb"

  # Full Release download URL
  url="${fioReleaseUri}/${path}/${filename}"

  echo
  echo
  echo "Install package will be downloaded from the FIO Releases repository located at ${fioReleaseUri}"
  pause

  # Clean any previous downloads
  rm -f $filename

  # Check that file is at uri/filename
  status=$( curl -L -o /dev/null --silent -Iw '%{http_code}' "${url}" )
  if [[ ${status} -ne 200 ]]; then
    echo
    echo "Curl exited with status: $status; File, ${filename}, was not found at URL ${url}!"
    echo
    return
  else
    echo "Downloading FIO installation package..."
    curl -L -sO "${url}"
  fi

  # Check a file was downloaded and is non-zero size
  minimumsize=13142400
  actualsize=$(wc -c <"$filename")
  if [[ -e ${filename} && ( $actualsize -le $minimumsize) ]]; then
    echo
    echo "The file size appears incorrect (minimum size: 13142400, actual size: $actualsize). Was the file download interrupted or exit with an error?"
    echo
    pause
  fi

  echo
  read -N 1 -p "Install package (y/N)? " answer
  echo
  if [[ "${answer,,}" == "n" ]]; then
    echo
    echo "You have elected to NOT install package, ${filename}."
    echo
    return
  fi

  echo
  echo "Installing FIO package..."
  echo
  apt install ./${filename}
  echo
  if [[ "testnet" == "${type}" ]]; then
    echo
    echo "Updating fio-nodeos configuration for TestNet"
    cp /etc/fio/nodeos/testnet-config.ini /etc/fio/nodeos/config.ini
    rm /etc/fio/nodeos/genesis.json
    ln -s /etc/fio/nodeos/genesis-testnet.json /etc/fio/nodeos/genesis.json
    echo
    echo "Note: Before starting fio-nodeos, verify that the nodeos configuration is"
    echo "appropriate for a ${type} install. Parameters may still be set for MainNet"
    echo "or may not be set at all, e.g. producer-name."
    echo "See /etc/fio/nodeos/config.ini, /etc/fio/nodeos/genesis.json"
    echo
  fi

  echo
  echo "It may be necessary to update the FIO configuration to;"
  echo "- Add producer name"
  echo "- Check settings"
  do_configure
}

function do_startstop() {
  local PS3="Select Action: "

  local items=("Start FIO" "Stop FIO" )

  select item in "${items[@]}" "Return to Main Menu"
  do
    echo
    echo -n "You've chosen to "
    case $REPLY in
      1) echo "$item"; do_start; break;;
      2) echo "$item"; do_stop; break;;
      $((${#items[@]}+1))) echo "Return to Main Menu"; echo; break 2;;
      *) echo "Ooops - unknown choice $REPLY"; break;
    esac
  done
}

function do_start() {
  # Start fio-nodeos
  echo
  echo "Starting the FIO nodeos application may require configuration updates. Skip the"
  echo "the following step if those have not yet been performed. The application may be"
  echo "started at a later time using the commands;"
  echo "  - sudo systemctl enable fio-nodeos"
  echo "  - sudo systemctl start fio-nodeos"
  echo
  echo "To stop the fio-nodeos application for any reason, run the command;"
  echo "  - sudo systemctl stop fio-nodeos"
  echo
  read -N 1 -p "Start service 'fio-nodeos' (y/N)? " answer
  if [[ "${answer,,}" == "y" ]]; then
    systemctl enable fio-nodeos
    systemctl start fio-nodeos
  fi

  echo
  echo
  echo "fio-wallet: The following step will start the fio-wallet service. Things to note are:"
  echo "  This service is not required to run fio-nodeos."
  echo "  This service is not needed unless background management of keys is required."
  echo "    If so, please note that this service is NOT an enterprise application and no"
  echo "    gaurantee is stated or implied!"
  echo "  The fio-wallet service may be started anytime or run on demand when necessary"
  echo "    either stand-alone or via the clio command."
  echo
  read -N 1 -p "Start service 'fio-wallet' (y/N)? " answer
  if [[ "${answer,,}" == "y" ]]; then
    systemctl enable fio-wallet
    systemctl start fio-wallet
  fi
  echo
  echo "Review '/var/log/fio/nodeos' for relevant blockchain logging."
  echo
  echo "FIO installation complete."
  echo
}

function do_stop() {
  local disable="${1}"

  read -N 1 -p "Stop service 'fio-nodeos' (y/N)? " answer
  if [[ "${answer,,}" == "y" ]]; then
    # Stop and disable fio-nodeo application
    echo
    echo "Stopping fio-nodeos..."
    systemctl stop fio-nodeos
    if [[ ${disable} ]]; then
      systemctl -q disable fio-nodeos
    fi
  fi

  read -N 1 -p "Stop service 'fio-wallet' (y/N)? " answer
  if [[ "${answer,,}" == "y" ]]; then
    # Stop and disable fio-wallet application
    echo
    echo "Stopping fio-wallet..."
    systemctl -q stop fio-wallet
    if [[ ${disable} ]]; then
      systemctl -q disable fio-wallet
    fi
  fi
}

function do_sync() {
  echo
  echo "Synchronizing from genesis means that your configured node will connect to the FIO P2P network,"
  echo "process blocks from the first block of the inception of the chain up to present time, then"
  echo "continue processing blocks as produced by the blockchain block producers."
  echo
  echo "To synchronize from genesis is only a matter of installing FIO, configuring FIO if needed,"
  echo "and starting the chain"
  echo
  echo "Note: Configuration is usually only needed when processing history during synchronization"
  do_configHelp "all"
  do_configure
}

# Install fio chain state, blocks.log and or history
function do_replay() {
  echo
  echo "You have elected to install blockchain data which may include state, a blocks.log"
  echo "and history. This will include download of an archive, extraction of the archive"
  echo "to '/var/lib/data' and confiugration of fio-nodeos."
  echo
  echo "Note that if startup from genesis is desired (synchronization from the p2p network),"
  echo "the following steps are not needed other than possible configuration update."
  echo

  local PS3="Select Action: "
  local items=("Replay from snapshot" "Replay from blocks.log" "Replay with V1 History" "Replay with State History" )
  while true; do
    select item in "${items[@]}" "Return to Main Menu"
    do
      echo
      echo -n "You've chosen to "
      case $REPLY in
        1) echo "$item"; do_state; break;;
        2) echo "$item"; do_blocks; break;;
        3) echo "$item"; do_v1history; break;;
        4) echo "$item"; do_statehistory; break;;
        $((${#items[@]}+1))) echo "Return to Main Menu!"; break 2;;
        *) echo "Ooops - unknown choice $REPLY"; break;
      esac
    done
  done
}

# Install fio chain state, blocks.log and or history
function do_state() {
  # Snapshot archive
  do_replayHelp "snapshot"

  pause
  echo
  echo "Downloading FIO snapshot archive..."
  getAndExtract ${fioArchiveUri} ${type}-latest-snap.txz
  if [[ $? -eq 0 ]]; then
    stateInstalled=true
  fi
  echo
  do_configHelp
}

function do_blocks() {
  # blocks.log and snapshot
  do_replayHelp "blocks.log"

  pause
  echo
  echo "Downloading FIO blocks.log archive..."
  getAndExtract ${fioArchiveUri} ${type}-latest-blocks.txz
  if [[ $? -eq 0 ]]; then
    stateInstalled=true
  fi
  echo
  do_configHelp
}

function do_v1history() {
  # v1 history, blocks.log, snapshot
  do_replayHelp "v1 history"

  pause
  echo
  echo "Downloading FIO History archive..."
  getAndExtract ${fioArchiveUri} ${type}-latest-history.txz
  if [[ $? -eq 0 ]]; then
    stateInstalled=true
  fi
  echo
  do_configHelp "v1"
}

# grep ":state_history_plugin" /etc/fio/nodeos/config.ini | egrep "^[[:space:]]*#" &>/dev/null
# if [[ $? -ne 0 ]]; then
#   sed -i '/exec/s/$/ --disable-replay-opts/' /usr/local/bin/fio-nodeos-run
# fi
function do_statehistory() {
  # blocks.log
  do_replayHelp "state history"
  
  pause
  echo
  pause
  echo
  echo "Downloading FIO blocks.log archive..."
  getAndExtract ${fioArchiveUri} ${type}-latest-blocks.txz
  if [[ $? -eq 0 ]]; then
    stateInstalled=true
	rm -f /var/lib/fio/data/blocks/blocks.index
  fi
  do_configHelp "state"
}

function do_replayHelp() {
  local replay="${1}"
  echo
  echo Installation from the lastest FIO ${replay} will:
  echo "  Remove any existing history, snapshot, state and blocks located in '/var/lib/fio'"
  echo "  Download the latest archive and install it into '/var/lib/fio'"
  echo "  Update ownership to be accessible by the fio-nodeos process"
  echo "  Remove the downloaded archive."
  echo
}

function do_start() {
  # Start fio-nodeos
  echo
  echo "Starting the FIO nodeos application may require configuration updates. Skip the"
  echo "the following step if those have not yet been performed. The application may be"
  echo "started at a later time using the commands;"
  echo "  - sudo systemctl enable fio-nodeos"
  echo "  - sudo systemctl start fio-nodeos"
  echo
  echo "To stop the fio-nodeos application for any reason, run the command;"
  echo "  - sudo systemctl stop fio-nodeos"
  echo
  read -N 1 -p "Start service 'fio-nodeos' (y/N)? " answer
  if [[ "${answer,,}" == "y" ]]; then
    systemctl enable fio-nodeos
    systemctl start fio-nodeos
  fi
  echo && echo
  echo "fio-wallet: The following step will start the fio-wallet service. Things to note are:"
  echo "  This service is not required to run fio-nodeos."
  echo "  This service is not needed unless background management of keys is required."
  echo "    If so, please note that this service is NOT an enterprise application and no"
  echo "    gaurantee is stated or implied!"
  echo "  The fio-wallet service may be started anytime or run on demand when necessary"
  echo "    either stand-alone or via the clio command."
  echo
  read -N 1 -p "Start service 'fio-wallet' (y/N)? " answer
  if [[ "${answer,,}" == "y" ]]; then
    systemctl enable fio-wallet
    systemctl start fio-wallet
  fi
  echo
  echo "Review '/var/log/fio/nodeos' for relevant blockchain logging."
  echo
  echo "FIO installation complete."
  echo
}

function do_stop() {
  systemctl stop fio-nodeos
  systemctl stop fio-wallet
}

# Constants, Variables

# FIO Releases
#fioReleaseUri="https://bin.fioprotocol.io" # S3 Bucket
fioReleaseUri="https://github.com/fioprotocol" # GitHub Releases

# FIO Snapshot, blocks.log, history archive URI
fioArchiveUri="https://snap.blockpane.com"

# TestNet/MainNet deployment type
type=""

# Booleans supporting install workflow
stateInstalled=false

# Main script functionality
PS3="Select Action: "

#items=("Install/Uninstall FIO" "Start/Stop FIO" "Install State" "Install Blocks.log" "Install V1 History" "Install State History" "Edit Config" )
items=("Install/Uninstall FIO" "Start/Stop FIO" "Sync From Genesis" "Replay From Archive" "Configure FIO" )

COLUMNS=1
while true; do
  select item in "${items[@]}" Quit
  do
    echo
    echo -n "You've chosen to "
    case $REPLY in
      1) echo "$item"; do_installuninstall; break;;
      2) echo "$item"; do_startstop; break;;
      3) echo "$item"; do_sync; break;;
      4) echo "$item"; do_replay; break;;
      5) echo "$item"; do_configure; break;;
      $((${#items[@]}+1))) echo "Exit!"; break 2;;
      *) echo "Ooops - unknown choice $REPLY"; break;
    esac
  done
done

What’s Next