github.com/FUSIONFoundation/efsn@v3.6.2-0.20200916075423-dbb5dd5d2cc7+incompatible/QuickNodeSetup/fsnNode.sh (about)

     1  #!/bin/bash
     2  # FUSION Foundation
     3  
     4  # no need for a complex versioning scheme, just increment
     5  SCRIPT_VERSION=10
     6  
     7  # historically grown, changing this would break stuff
     8  BASE_DIR="/home/$USER"
     9  
    10  # global configuration file containing node settings
    11  CONF_FILE="$BASE_DIR/fusion-node/node.json"
    12  
    13  # set to 1 to enable debug mode, or use -d argument
    14  DEBUG_MODE=0
    15  
    16  txtred=$(tput setaf 1)    # Red
    17  txtgrn=$(tput setaf 2)    # Green
    18  txtylw=$(tput setaf 3)    # Yellow
    19  txtrst=$(tput sgr0)       # Text reset
    20  
    21  while getopts ":d" opt; do
    22      case $opt in
    23           d) DEBUG_MODE=1 ;;
    24          \?) echo "${txtred}Invalid argument: -$OPTARG${txtrst}" ; exit 1 ;;
    25      esac
    26  done
    27  
    28  [ $DEBUG_MODE -eq 1 ] && set -x
    29  
    30  scriptUpdate() {
    31      # get the first 150 bytes and extract the script version
    32      remoteVersion="$(curl -fsSL -r 0-150 "https://raw.githubusercontent.com/FUSIONFoundation/efsn/master/QuickNodeSetup/fsnNode.sh" | grep -Po '(?<=SCRIPT_VERSION=)[0-9]+')"
    33      # prevent an error if the version number can't be read
    34      if [ -n "$remoteVersion" ]; then
    35          if [ $SCRIPT_VERSION -lt $remoteVersion ]; then
    36              echo "tbd"
    37          fi
    38      fi
    39  }
    40  
    41  distroChecks() {
    42      if ! command -v lsb_release >/dev/null 2>&1; then
    43          echo "${txtred}Missing command 'lsb_release', please install it${txtrst}"
    44          exit 1
    45      fi
    46      # check for distribution and corresponding version (release)
    47      distroID="$(lsb_release -si)"
    48      distroMajorVerion="$(lsb_release -sr | sed -E 's/([0-9]+).*/\1/')"
    49      if [ "$distroID" = "Ubuntu" ]; then
    50          if [ $distroMajorVerion -lt 18 ]; then
    51              echo "${txtred}Warning: Unsupported Ubuntu release${txtrst}"
    52              echo "Currently supported: Ubuntu 18.04 or newer"
    53              # exit 1
    54          fi
    55      elif [ "$distroID" = "CentOS" ]; then
    56          if [ $distroMajorVerion -lt 7 ]; then
    57              echo "${txtred}Warning: Unsupported CentOS release${txtrst}"
    58              echo "Currently supported: CentOS 7 or newer"
    59              # exit 1
    60          fi
    61      else
    62          echo "${txtred}Warning: May be unsupported distribution${txtrst}"
    63          echo "Ubuntu and CentOS is advised"
    64          #exit 1 # don't forbid other OS systems but warning
    65      fi
    66  }
    67  
    68  dependChecks() {
    69      echo
    70      echo "${txtylw}Checking dependencies${txtrst}"
    71  
    72      if ! command -v docker >/dev/null 2>&1; then
    73          echo "${txtred}Missing command 'docker', please install it${txtrst}"
    74          exit 1
    75      fi
    76  
    77      neededCmds="curl jq locate"
    78      neededPkgs="ca-certificates curl jq mlocate"
    79      missingCmds=""
    80      for cmd in $neededCmds; do
    81          if ! command -v $cmd >/dev/null 2>&1; then
    82              missingCmds="$missingCmds $cmd"
    83          fi
    84      done
    85  
    86      if [ -n "$missingCmds" ]; then
    87          if command -v apt >/dev/null 2>&1; then
    88              sudo apt install -q -y $neededPkgs
    89          elif command -v yum >/dev/null 2>&1; then
    90              sudo yum install -q -y $neededPkgs
    91          else
    92              echo "${txtred}Please install missing commands: $missingCmds${txtrst}"
    93              exit 1
    94          fi
    95      fi
    96  
    97      echo "${txtred}✓${txtrst} Check dependencies passed"
    98  }
    99  
   100  sanityChecks() {
   101      if [ -z "$BASH" ]; then
   102          echo "${txtred}The setup script has to be run in the bash shell.${txtrst}"
   103          echo "Please run it again in bash:"
   104          echo "bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/FUSIONFoundation/efsn/master/QuickNodeSetup/fsnNode.sh)\""
   105          exit 1
   106      fi
   107  
   108      # checking the effective user id, where 0 is root
   109      if [ $EUID -ne 0 ]; then
   110          # make sure that the script isn't run as root and non-root user alternately
   111          if sudo [ -f "/home/root/fusion-node/node.json" ]; then
   112              echo "${txtred}Warning: The setup script was originally run with root privileges.${txtrst}"
   113              echo "We suggest you to run it again as user root or by invoking sudo:"
   114              echo "sudo bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/FUSIONFoundation/efsn/master/QuickNodeSetup/fsnNode.sh)\""
   115              echo
   116              local question="${txtylw}Are you sure you want to continue as user $USER?${txtrst} [Y/n] "
   117              askToContinue "$question"
   118              if [ $? -eq 1 ]; then
   119                  exit 1
   120              fi
   121          fi
   122      fi
   123  
   124      # using locate without prior update is a perf vs reliability tradeoff
   125      # we don't want to wait until the whole fs is indexed or even use find
   126      if [ $(sudo locate -r .*/efsn/chaindata$ -c) -gt 1 ]; then
   127          echo "${txtred}Warning: Found more than one chaindata directory.${txtrst}"
   128          echo "Please check and clean up the system manually first."
   129          sudo locate -r .*/efsn/chaindata$
   130  
   131          echo
   132          local question="${txtylw}Do you believe this issue is already resolved?${txtrst} [Y/n] "
   133          askToContinue "$question"
   134          if [ $? -eq 1 ]; then
   135              exit 1
   136          fi
   137      fi
   138  }
   139  
   140  getDockerImageName() {
   141      local nodetype="$1"
   142      local testnet="$2"
   143      case $nodetype in
   144          "minerandlocalgateway")
   145              if [ "$testnet" = "true" ]; then
   146                  echo "fusionnetwork/testnet-minerandlocalgateway"
   147              else
   148                  echo "fusionnetwork/minerandlocalgateway"
   149              fi
   150              ;;
   151          "efsn")
   152              if [ "$testnet" = "true" ]; then
   153                  echo "fusionnetwork/testnet-efsn"
   154              else
   155                  echo "fusionnetwork/efsn"
   156              fi
   157              ;;
   158          "gateway")
   159              if [ "$testnet" = "true" ]; then
   160                  echo "fusionnetwork/testnet-gateway"
   161              else
   162                  echo "fusionnetwork/gateway"
   163              fi
   164              ;;
   165          *) echo ""
   166              ;;
   167      esac
   168  }
   169  
   170  checkUpdate() {
   171      local nodetype="$(getCfgValue 'nodeType')"
   172      local testnet="$(getCfgValue 'testnet')"
   173      local imagename="$(getDockerImageName $nodetype $testnet)"
   174  
   175      # get the container creation date as unix epoch for easy comparison
   176      local dateCreated=$(date -d "$(sudo docker container inspect -f "{{.Created}}" fusion 2>/dev/null)" '+%s')
   177      # query the Docker Hub registry for when the image was last updated
   178      local dateUpdated="$(curl -fsL "https://registry.hub.docker.com/v2/repositories/$imagename" | jq -r '.last_updated')"
   179      # make sure that no update is triggered if the registry returns no data
   180      [ -z "$dateUpdated" ] && dateUpdated=0 || dateUpdated=$(date -d "$dateUpdated" '+%s')
   181      # if the container is older than dateUpdated return 0, otherwise return 1
   182      [ $dateCreated -lt $dateUpdated ] && return 0 || return 1
   183  }
   184  
   185  pauseScript() {
   186      local fackEnterKey
   187      read -s -p "${txtylw}Press [Enter] to continue...${txtrst}" fackEnterKey
   188  }
   189  
   190  askToContinue() {
   191      local input
   192      while true; do
   193          read -n1 -r -s -p "$1" input
   194          case $input in
   195              [yY]) echo; return 0 ;;
   196              [nN]) echo; return 1 ;;
   197              *)    echo -e "\n${txtred}Invalid input${txtrst}" ;;
   198          esac
   199      done
   200  }
   201  
   202  getCfgValue() {
   203      # read configuration values from the respective JSON files
   204      local cfg_arg="$1"
   205      local cfg_val
   206  
   207      if [ $# -ne 1 ]; then
   208          echo "${txtred}Incorrect number of arguments ($#)${txtrst}"
   209          return 1
   210      fi
   211  
   212      local keystore_file="$BASE_DIR/fusion-node/data/keystore/UTC.json"
   213      local cfg_file="$CONF_FILE"
   214  
   215      # extra spacing for readability
   216      if [ "$cfg_arg" = "address" ]; then
   217          cfg_val="0x$(jq -r '.address' $keystore_file)"
   218  
   219      elif [ "$cfg_arg" = "nodeType" ]; then
   220          cfg_val="$(jq -r '.nodeType' $cfg_file)"
   221  
   222      elif [ "$cfg_arg" = "testnet" ]; then
   223          cfg_val="$(jq -r '.testnet' $cfg_file)"
   224  
   225      elif [ "$cfg_arg" = "autobt" ]; then
   226          cfg_val="$(jq -r '.autobt' $cfg_file)"
   227  
   228      elif [ "$cfg_arg" = "mining" ]; then
   229          cfg_val="$(jq -r '.mining' $cfg_file)"
   230  
   231      elif [ "$cfg_arg" = "nodeName" ]; then
   232          cfg_val="$(jq -r '.nodeName' $cfg_file)"
   233  
   234      else
   235          echo "${txtred}Unknown argument ($cfg_arg)${txtrst}"
   236          return 1
   237      fi
   238  
   239      echo "$cfg_val"
   240  }
   241  
   242  putCfgValue() {
   243      # write configuration values to the respective JSON files
   244      local cfg_arg="$1"
   245      local cfg_val="$2"
   246  
   247      if [ $# -ne 2 ]; then
   248          echo "${txtred}Incorrect number of arguments ($#)${txtrst}"
   249          return 1
   250      fi
   251  
   252      # create main node data directory
   253      mkdir -p "$BASE_DIR/fusion-node/"
   254  
   255      local cfg_file="$CONF_FILE"
   256  
   257      # create minimal JSON skeleton for jq if the config file doesn't exist yet
   258      if [ ! -f "$cfg_file" ]; then
   259          echo '{}' > "$cfg_file"
   260      fi
   261  
   262      # extra spacing for readability
   263      if [ "$cfg_arg" = "nodeType" ]; then
   264          cat <<< "$(jq --arg nodeType "$cfg_val" '.nodeType = $nodeType' < $cfg_file)" > "$cfg_file"
   265  
   266      elif [ "$cfg_arg" = "testnet" ]; then
   267          cat <<< "$(jq --arg testnet "$cfg_val" '.testnet = $testnet' < $cfg_file)" > "$cfg_file"
   268  
   269      elif [ "$cfg_arg" = "autobt" ]; then
   270          cat <<< "$(jq --arg autobt "$cfg_val" '.autobt = $autobt' < $cfg_file)" > "$cfg_file"
   271  
   272      elif [ "$cfg_arg" = "mining" ]; then
   273          cat <<< "$(jq --arg mining "$cfg_val" '.mining = $mining' < $cfg_file)" > "$cfg_file"
   274  
   275      elif [ "$cfg_arg" = "nodeName" ]; then
   276          cat <<< "$(jq --arg nodeName "$cfg_val" '.nodeName = $nodeName' < $cfg_file)" > "$cfg_file"
   277  
   278      else
   279          echo "${txtred}Unknown argument ($cfg_arg)${txtrst}"
   280          return 1
   281      fi
   282  }
   283  
   284  updateKeystoreFile() {
   285      echo
   286      echo "${txtylw}Open your keystore file (its name usually starts with UTC--) in any"
   287      echo "plaintext editor (like notepad) and copy and paste its full contents"
   288      echo "here; it should contain a lot of cryptic text surrounded by \"{...}\".${txtrst}"
   289      local keystorejson
   290      while true; do
   291          read -p "Paste keystore contents: " keystorejson
   292          if [ -z "$keystorejson" ]; then
   293              echo "${txtred}Keystore contents required${txtrst}"
   294          elif [[ "$keystorejson" != *"address"* ]]; then
   295              echo "${txtred}Invalid keystore format${txtrst}"
   296          elif ! jq -e . >/dev/null 2>&1 <<< "$keystorejson"; then
   297              echo "${txtred}Invalid keystore format${txtrst}"
   298          else
   299              # this breaks for me - avoid xargs if possible
   300              #echo $keystorejson | xargs > "$BASE_DIR/fusion-node/data/keystore/UTC.json"
   301              # this should be a pretty robust solution
   302              mkdir -p "$BASE_DIR/fusion-node/data/keystore/"
   303              printf '%s' "$keystorejson" > "$BASE_DIR/fusion-node/data/keystore/UTC.json"
   304  
   305              echo
   306              local address="$(getCfgValue 'address')"
   307              local question="${txtylw}Is this the expected address of your staking wallet?${txtrst} $address [Y/n] "
   308              askToContinue "$question"
   309              if [ $? -eq 1 ]; then
   310                  echo "${txtred}Please use the right keystore file${txtrst}"
   311              else
   312                  break
   313              fi
   314          fi
   315      done
   316  }
   317  
   318  updateKeystorePass() {
   319      echo
   320      echo "${txtylw}Please enter or paste the password that unlocks the keystore file.${txtrst}"
   321      local keystorepass
   322      while true; do
   323          read -p "Enter keystore password: " keystorepass
   324          if [ -z "$keystorepass" ]; then
   325              echo "${txtred}Keystore password required${txtrst}"
   326          else
   327              break
   328          fi
   329      done
   330      printf '%s' "$keystorepass" > "$BASE_DIR/fusion-node/password.txt"
   331  }
   332  
   333  updateExplorerListing() {
   334      echo
   335      question="${txtylw}Do you want your node to be listed on the node explorer?${txtrst} [Y/n] "
   336      local nodename
   337      askToContinue "$question"
   338      if [ $? -eq 0 ]; then
   339          echo
   340          echo "${txtylw}What name do you want the node to have? No spaces or special characters, three characters minimum.${txtrst}"
   341          while true; do
   342              read -p "Enter node name: " nodename
   343              if [ -z "$nodename" ]; then
   344                  echo "${txtred}Node name required${txtrst}"
   345              elif [[ ! "$nodename" =~ ^[-_a-zA-Z0-9]{3,}$ ]]; then
   346                  echo "${txtred}Invalid characters in node name or too short${txtrst}"
   347                  echo "Only a-z, 0-9, - and _ allowed, minimum 3 chars"
   348              else
   349                  break
   350              fi
   351          done
   352          echo "${txtgrn}✓${txtrst} The node will be listed on the node explorer as \"$nodename\""
   353      else
   354          echo "${txtred}✓${txtrst} The node will not be listed on the node explorer"
   355      fi
   356      putCfgValue 'nodeName' "$nodename"
   357  }
   358  
   359  initConfig() {
   360      local question
   361  
   362      # purge existing node data; the user was warned about this,
   363      # he can reconfigure the node if that's not what he wants
   364      sudo rm -rf "$BASE_DIR/fusion-node/"
   365  
   366      echo
   367      echo "${txtylw}Please select the node type to install:${txtrst}"
   368      echo "${txtylw}1. minerandlocalgateway${txtrst} - miner with local API access; if unsure, select this"
   369      echo "${txtylw}2. efsn${txtrst} - pure miner without local API access; only select if you have a good reason"
   370      echo "${txtylw}3. gateway${txtrst} - local API access without mining; doesn't require keystore and password"
   371      local nodetype
   372      local input
   373      while true; do
   374          read -n1 -r -s -p "Select option [1-3] " input
   375          case $input in
   376              1) nodetype="minerandlocalgateway"; break ;;
   377              2) nodetype="efsn"; break ;;
   378              3) nodetype="gateway"; break ;;
   379              *) echo -e "\n${txtred}Invalid input${txtrst}" ;;
   380          esac
   381      done
   382      echo
   383      echo "${txtgrn}✓${txtrst} Selected node type $nodetype"
   384      putCfgValue 'nodeType' "$nodetype"
   385  
   386      echo
   387      question="${txtylw}Do you want to install a mainnet node?${txtrst} [Y/n] "
   388      local testnet="false"
   389      askToContinue "$question"
   390      if [ $? -eq 0 ]; then
   391          echo "${txtgrn}✓${txtrst} Installing mainnet node"
   392      else
   393          echo
   394          question="${txtylw}Are you sure you want to install a testnet node?${txtrst} [Y/n] "
   395          askToContinue "$question"
   396          if [ $? -eq 0 ]; then
   397              testnet="true"
   398              echo "${txtgrn}✓${txtrst} Installing testnet node"
   399          else
   400              echo "${txtgrn}✓${txtrst} Installing mainnet node"
   401          fi
   402      fi
   403      putCfgValue 'testnet' "$testnet"
   404  
   405      if [ "$nodetype" = "minerandlocalgateway" -o "$nodetype" = "efsn" ]; then
   406          updateKeystoreFile
   407          echo "${txtgrn}✓${txtrst} Saved keystore file"
   408  
   409          updateKeystorePass
   410          echo "${txtgrn}✓${txtrst} Saved keystore password"
   411  
   412          echo
   413          question="${txtylw}Do you want your node to auto-buy tickets required for staking?${txtrst} [Y/n] "
   414          local autobuy="false"
   415          askToContinue "$question"
   416          if [ $? -eq 0 ]; then
   417              autobuy="true"
   418              echo "${txtgrn}✓${txtrst} Enabled ticket auto-buy"
   419          else
   420              echo "${txtred}✓${txtrst} Disabled ticket auto-buy"
   421          fi
   422          putCfgValue 'autobt' "$autobuy"
   423  
   424          echo
   425          question="${txtylw}Do you want your node to mine blocks (participate in staking)?${txtrst} [Y/n] "
   426          local mining="false"
   427          askToContinue "$question"
   428          if [ $? -eq 0 ]; then
   429              mining="true"
   430              echo "${txtgrn}✓${txtrst} Enabled mining of blocks"
   431          else
   432              echo "${txtred}✓${txtrst} Disabled mining of blocks"
   433          fi
   434          putCfgValue 'mining' "$mining"
   435      fi
   436  
   437      echo
   438      question="${txtylw}Do you want your node to auto-start after boot to prevent downtimes?${txtrst} [Y/n] "
   439      askToContinue "$question"
   440      if [ $? -eq 0 ]; then
   441          if [ ! -f "/etc/systemd/system/fusion.service" ]; then
   442              sudo curl -fsSL "https://raw.githubusercontent.com/FUSIONFoundation/efsn/master/QuickNodeSetup/fusion.service" \
   443                  -o "/etc/systemd/system/fusion.service"
   444          fi
   445          sudo systemctl daemon-reload
   446          sudo systemctl -q enable fusion
   447          echo "${txtgrn}✓${txtrst} Enabled node auto-start"
   448      else
   449          # suppress warning about nonexisting service unit on fresh install
   450          sudo systemctl -q disable fusion 2>/dev/null
   451          echo "${txtred}✓${txtrst} Disabled node auto-start"
   452      fi
   453  
   454      updateExplorerListing
   455  }
   456  
   457  removeContainer() {
   458      # only try to stop the container if it's running
   459      [ "$(sudo docker container inspect -f "{{.State.Running}}" fusion 2>/dev/null)" = "true" ] && stopNode
   460      # remove container and base images no matter what
   461      echo
   462      echo "${txtylw}Removing container and base images${txtrst}"
   463      sudo docker rm fusion >/dev/null 2>&1
   464      # mainnet images
   465      sudo docker rmi fusionnetwork/minerandlocalgateway \
   466          fusionnetwork/efsn \
   467          fusionnetwork/gateway >/dev/null 2>&1
   468      sudo docker rmi fusionnetwork/minerandlocalgateway2 \
   469          fusionnetwork/efsn2 \
   470          fusionnetwork/gateway2 >/dev/null 2>&1
   471      # testnet images
   472      sudo docker rmi fusionnetwork/testnet-minerandlocalgateway \
   473          fusionnetwork/testnet-efsn \
   474          fusionnetwork/testnet-gateway >/dev/null 2>&1
   475      echo "${txtgrn}✓${txtrst} Removed container and base images"
   476  }
   477  
   478  createContainer() {
   479      # read configuration files
   480      echo
   481      echo "${txtylw}Reading node configuration${txtrst}"
   482      local nodetype="$(getCfgValue 'nodeType')"
   483      local testnet="$(getCfgValue 'testnet')"
   484      local nodename="$(getCfgValue 'nodeName')"
   485      if [ "$nodetype" = "minerandlocalgateway" -o "$nodetype" = "efsn" ]; then
   486          local autobuy="$(getCfgValue 'autobt')"
   487          local mining="$(getCfgValue 'mining')"
   488          local address="$(getCfgValue 'address')"
   489      fi
   490      echo "${txtgrn}✓${txtrst} Read node configuration"
   491      local imagename="$(getDockerImageName $nodetype $testnet)"
   492  
   493      if [ "$testnet" = "true" ]; then
   494          # run testnet node
   495          testnet="--testnet"
   496      else
   497          # run mainnet node
   498          testnet=""
   499      fi
   500  
   501      if [ "$autobuy" = "true" ]; then
   502          # turn autobuy on
   503          autobuy="--autobt"
   504      else
   505          # turn autobuy off
   506          autobuy=""
   507      fi
   508  
   509      # make sure mining remains enabled for old configs
   510      if [ "$mining" != "false" ]; then
   511          # turn mining on
   512          mining=""
   513      else
   514          # turn mining off
   515          mining="--disable-mining"
   516      fi
   517  
   518      echo
   519      echo "${txtylw}Creating node container from image $imagename${txtrst}"
   520      if [ "$nodetype" = "minerandlocalgateway" ]; then
   521          # docker create automatically pulls the image if it's not there
   522          # we do not need -i here as it's not really an interactive terminal
   523          sudo docker create --name fusion -t --restart unless-stopped \
   524              -p 127.0.0.1:9000:9000 -p 127.0.0.1:9001:9001 -p 40408:40408 -p 40408:40408/udp \
   525              -v "$BASE_DIR/fusion-node":/fusion-node \
   526              $imagename \
   527              -u "$address" $testnet $autobuy $mining \
   528              -e "$nodename"
   529  
   530      elif [ "$nodetype" = "efsn" ]; then
   531          sudo docker create --name fusion -t --restart unless-stopped \
   532              -p 40408:40408 -p 40408:40408/udp \
   533              -v "$BASE_DIR/fusion-node":/fusion-node \
   534              $imagename \
   535              -u "$address" $testnet $autobuy $mining \
   536              -e "$nodename"
   537  
   538      elif [ "$nodetype" = "gateway" ]; then
   539          # one could optionally create a public gateway, but that should always utilize
   540          # encryption which requires a properly configured reverse TLS proxy like nginx
   541          sudo docker create --name fusion -t --restart unless-stopped \
   542              -p 127.0.0.1:9000:9000 -p 127.0.0.1:9001:9001 -p 40408:40408 -p 40408:40408/udp \
   543              -v "$BASE_DIR/fusion-node":/fusion-node \
   544              $imagename \
   545              $testnet \
   546              -e "$nodename"
   547  
   548          # workaround for the breaking change introduced with https://github.com/FUSIONFoundation/efsn/commit/8fab78ee3872be05cda3c53db24a49ddea0dfe98
   549          # originally the gateway did not use an entrypoint script which defined a data subdirectory; this prevents two things:
   550          # 1) orphaned chaindata wasting disk space
   551          # 2) a full resync because chaindata is gone
   552          if [ -d "$BASE_DIR/fusion-node/efsn" ]; then
   553              if [ -d "$BASE_DIR/fusion-node/data/efsn" ]; then
   554                  sudo rm -rf "$BASE_DIR/fusion-node/efsn/"
   555              else
   556                  sudo mkdir -p "$BASE_DIR/fusion-node/data/"
   557                  sudo mv "$BASE_DIR/fusion-node/efsn/" "$BASE_DIR/fusion-node/data/efsn"
   558              fi
   559              sudo rm -rf "$BASE_DIR/fusion-node/efsn.ipc" "$BASE_DIR/fusion-node/keystore/"
   560          fi
   561  
   562      else
   563          echo "${txtred}Invalid node type${txtrst}"
   564          exit 1
   565      fi
   566  
   567      # check the container is really created. it may fail as network reasons.
   568      createdTime="$(sudo docker container inspect -f "{{.Created}}" fusion 2>/dev/null)"
   569      if [ -z "$createdTime" ]; then
   570          echo "${txtred}Create container failed, please check your network${txtrst}"
   571          exit 1
   572      fi
   573  
   574      # reset global update variable
   575      hasUpdate=1
   576      echo "${txtgrn}✓${txtrst} Created node container"
   577  }
   578  
   579  startNode() {
   580      echo
   581      echo "${txtylw}Starting the node${txtrst}"
   582      sudo docker start fusion >/dev/null
   583      if [ $? -eq 0 ]; then
   584          echo "${txtgrn}✓${txtrst} Node started"
   585          echo
   586          echo "---------------------------------------------------------------"
   587          echo "| Please use the \"Show node logs\" function from the main menu |"
   588          echo "|  to verify that the node is really running without errors!  |"
   589          echo "---------------------------------------------------------------"
   590      else
   591          echo "${txtred}Node failed to start${txtrst}"
   592      fi
   593  }
   594  
   595  stopNode() {
   596      echo
   597      echo "${txtylw}Stopping the node${txtrst}"
   598      echo "This might take a moment, please wait..."
   599      sudo docker stop fusion >/dev/null
   600      if [ $? -eq 0 ]; then
   601          echo "${txtgrn}✓${txtrst} Node stopped"
   602      else
   603          echo "${txtred}Node failed to stop${txtrst}"
   604      fi
   605  }
   606  
   607  installNode() {
   608      [ $DEBUG_MODE -ne 1 ] && clear
   609      echo
   610      echo "---------------------"
   611      echo "| Node Installation |"
   612      echo "---------------------"
   613  
   614      if [ -d "$BASE_DIR/fusion-node" ]; then
   615          echo
   616          echo "${txtylw}You already seem to have a node installed in $BASE_DIR/fusion-node.${txtrst}"
   617          echo "It will be stopped and its configuration and chaindata will be purged."
   618          echo "This means it has to sync from scratch again, which could take a while."
   619          echo "Please look into the \"configure node\" menu if you want to change node"
   620          echo "configuration settings which don't require a full reset."
   621          echo
   622          local question="${txtylw}Are you sure you want to continue?${txtrst} [Y/n] "
   623          askToContinue "$question"
   624          if [ $? -eq 1 ]; then
   625              echo "${txtred}✓${txtrst} Installation cancelled"
   626              return 1
   627          fi
   628      fi
   629  
   630      mkdir -p "$BASE_DIR/"
   631      local spacefree=$(df -B 1024k --output=avail $BASE_DIR | egrep -o '[0-9]+')
   632      local spaceoccup=$(sudo du -B 1024k --summarize $BASE_DIR | awk '{print $1}')
   633      local spaceavail=$(expr $spacefree + $spaceoccup)
   634      if [ $spaceavail -lt 25000 ]; then
   635          echo
   636          echo "${txtylw}You seem to have less than 25GB of free storage available for chaindata in $BASE_DIR.${txtrst}"
   637          echo "If the node runs out of disk space, it will stop syncing and your tickets might get retreated."
   638          echo
   639          local question="${txtylw}Are you sure you want to continue?${txtrst} [Y/n] "
   640          askToContinue "$question"
   641          if [ $? -eq 1 ]; then
   642              echo "${txtred}✓${txtrst} Installation cancelled"
   643              return 1
   644          fi
   645      fi
   646  
   647      local memavail=$(awk '/^MemAvailable/ {print $2}' /proc/meminfo)
   648      if [ $memavail -lt 2097152 ]; then
   649          echo
   650          echo "${txtylw}You seem to have less than 2GB of free memory available.${txtrst}"
   651          echo "If the node runs out of memory, it will crash and your tickets might get retreated."
   652          echo
   653          local question="${txtylw}Are you sure you want to continue?${txtrst} [Y/n] "
   654          askToContinue "$question"
   655          if [ $? -eq 1 ]; then
   656              echo "${txtred}✓${txtrst} Installation cancelled"
   657              return 1
   658          fi
   659      fi
   660  
   661      echo
   662      echo "<<< Installing node >>>"
   663      initConfig
   664      removeContainer
   665      createContainer
   666      startNode
   667      echo
   668      echo "<<< ${txtgrn}✓${txtrst} Installed node >>>"
   669      echo
   670      pauseScript
   671  }
   672  
   673  deinstallNode() {
   674      [ $DEBUG_MODE -ne 1 ] && clear
   675      echo
   676      echo "-----------------------"
   677      echo "| Node Deinstallation |"
   678      echo "-----------------------"
   679  
   680      if [ -d "$BASE_DIR/fusion-node" ]; then
   681          echo
   682          echo "You already seem to have a node installed in $BASE_DIR/fusion-node."
   683          echo "It will be stopped and its configuration and chaindata will be purged."
   684          echo
   685          local question="${txtylw}Are you sure you want to continue?${txtrst} [Y/n] "
   686          askToContinue "$question"
   687          if [ $? -eq 1 ]; then
   688              echo "${txtred}✓${txtrst} Deinstallation cancelled"
   689              return 1
   690          fi
   691      fi
   692  
   693      echo
   694      echo "<<< Deinstalling node >>>"
   695      sudo rm -rf "$BASE_DIR/fusion-node/"
   696      removeContainer
   697      sudo systemctl -q stop fusion
   698      sudo systemctl -q disable fusion
   699      sudo systemctl daemon-reload
   700      sudo rm -f "/etc/systemd/system/fusion.service"
   701      echo
   702      echo "<<< ${txtgrn}✓${txtrst} Deinstalled node >>>"
   703      echo
   704      pauseScript
   705  }
   706  
   707  updateNode() {
   708      echo
   709      echo "<<< Updating node >>>"
   710      removeContainer
   711      createContainer
   712      startNode
   713      echo
   714      echo "<<< ${txtgrn}✓${txtrst} Updated node >>>"
   715  }
   716  
   717  updateNodeScreen() {
   718      [ $DEBUG_MODE -ne 1 ] && clear
   719      echo
   720      echo "---------------"
   721      echo "| Node Update |"
   722      echo "---------------"
   723      updateNode
   724      echo
   725      pauseScript
   726  }
   727  
   728  showNodeLogs() {
   729      [ $DEBUG_MODE -ne 1 ] && clear
   730      echo
   731      echo "-------------------"
   732      echo "| Node Log Viewer |"
   733      echo "-------------------"
   734      echo
   735      echo "${txtylw}Press Ctrl + C to quit!${txtrst}"
   736      echo
   737      pauseScript
   738      echo
   739      echo
   740      # entering a subshell here so killing the logger doesn't exit the main script
   741      (
   742          # restoring SIGINT so Ctrl + C works
   743          trap - SIGINT
   744          # we do not have to attach because we don't need interactive access
   745          # this also has the advantage that the node doesn't need to be running
   746          sudo docker logs fusion --tail=25 -f
   747          if [ $? -ne 0 ]; then
   748              echo "${txtred}Failed to show node log${txtrst}"
   749          fi
   750          # ignoring SIGINT again
   751          trap '' SIGINT
   752      )
   753      echo
   754      echo
   755      pauseScript
   756  }
   757  
   758  change_autobuy() {
   759      local nodetype="$(getCfgValue 'nodeType')"
   760      if [ "$nodetype" = "minerandlocalgateway" -o "$nodetype" = "efsn" ]; then
   761          local autobuy="$(getCfgValue 'autobt')"
   762          local state
   763          # just making the output a bit nicer
   764          [ "$autobuy" = "true" ] && state="${txtgrn}enabled${txtrst}" || state="${txtred}disabled${txtrst}"
   765          echo
   766          echo "Ticket auto-buy is currently $state"
   767          # we can't just use the API here as an unexpected node restart would cause problems
   768          local question="${txtylw}Do you want to change this setting? Doing so will enforce a node update and restart!${txtrst} [Y/n] "
   769          askToContinue "$question"
   770          if [ $? -eq 0 ]; then
   771              echo
   772              echo "<<< Changing auto-buy setting >>>"
   773              [ "$autobuy" = "true" ] && autobuy="false" || autobuy="true"
   774              putCfgValue 'autobt' "$autobuy"
   775              updateNode
   776              echo
   777              echo "<<< ${txtgrn}✓${txtrst} Changed auto-buy setting >>>"
   778              echo
   779              pauseScript
   780          fi
   781      else
   782          echo
   783          echo "This setting is only available for minerandlocalgateway and efsn node types"
   784          echo
   785          pauseScript
   786      fi
   787  }
   788  
   789  change_mining() {
   790      local nodetype="$(getCfgValue 'nodeType')"
   791      if [ "$nodetype" = "minerandlocalgateway" -o "$nodetype" = "efsn" ]; then
   792          local mining="$(getCfgValue 'mining')"
   793          local state
   794          # just making the output a bit nicer
   795          [ "$mining" != "false" ] && state="${txtgrn}enabled${txtrst}" || state="${txtred}disabled${txtrst}"
   796          echo
   797          echo "Mining of new blocks is currently $state"
   798          # we can't just use the API here as an unexpected node restart would cause problems
   799          local question="${txtylw}Do you want to change this setting? Doing so will enforce a node update and restart!${txtrst} [Y/n] "
   800          askToContinue "$question"
   801          if [ $? -eq 0 ]; then
   802              echo
   803              echo "<<< Changing mining setting >>>"
   804              # make sure mining remains enabled for old configs
   805              [ "$mining" != "false" ] && mining="false" || mining="true"
   806              putCfgValue 'mining' "$mining"
   807              updateNode
   808              echo
   809              echo "<<< ${txtgrn}✓${txtrst} Changed mining setting >>>"
   810              echo
   811              pauseScript
   812          fi
   813      else
   814          echo
   815          echo "This setting is only available for minerandlocalgateway and efsn node types"
   816          echo
   817          pauseScript
   818      fi
   819  }
   820  
   821  change_wallet() {
   822      local nodetype="$(getCfgValue 'nodeType')"
   823      if [ "$nodetype" = "minerandlocalgateway" -o "$nodetype" = "efsn" ]; then
   824          local address="$(getCfgValue 'address')"
   825          echo
   826          echo "The current staking wallet address is ${txtgrn}$address${txtrst}"
   827          # we can't just use the API here as an unexpected node restart would cause problems
   828          local question="${txtylw}Do you want to change this setting? Doing so will enforce a node update and restart!${txtrst} [Y/n] "
   829          askToContinue "$question"
   830          if [ $? -eq 0 ]; then
   831              echo
   832              echo "<<< Changing staking wallet >>>"
   833              updateKeystoreFile
   834              echo "${txtgrn}✓${txtrst} Updated keystore file"
   835              updateKeystorePass
   836              echo "${txtgrn}✓${txtrst} Updated keystore password"
   837              updateNode
   838              echo
   839              echo "<<< ${txtgrn}✓${txtrst} Changed staking wallet >>>"
   840              echo
   841              pauseScript
   842          fi
   843      else
   844          echo
   845          echo "This setting is only available for minerandlocalgateway and efsn node types"
   846          echo
   847          pauseScript
   848      fi
   849  }
   850  
   851  change_autostart() {
   852      local state="$(systemctl show fusion -p UnitFileState --value)"
   853      # just making the output a bit nicer
   854      local statemsg
   855      [ "$state" = "enabled" ] && statemsg="${txtgrn}enabled${txtrst}" || statemsg="${txtred}disabled${txtrst}"
   856      echo
   857      echo "Node auto-start is currently $statemsg"
   858      local question="${txtylw}Do you want to change this setting?${txtrst} [Y/n] "
   859      askToContinue "$question"
   860      if [ $? -eq 0 ]; then
   861          echo
   862          echo "<<< Changing auto-start setting >>>"
   863          # if auto-start wasn't enabled during installation, state will be empty here
   864          if [ "$state" != "enabled" ]; then
   865              # download systemd service unit definition if it doesn't exist yet
   866              if [ ! -f "/etc/systemd/system/fusion.service" ]; then
   867                  sudo curl -fsSL "https://raw.githubusercontent.com/FUSIONFoundation/efsn/master/QuickNodeSetup/fusion.service" \
   868                      -o "/etc/systemd/system/fusion.service"
   869              fi
   870              # reload systemd service unit definitions
   871              sudo systemctl daemon-reload
   872              # enable the systemd service unit
   873              sudo systemctl -q enable fusion
   874          else
   875              # disable the systemd service unit
   876              sudo systemctl -q disable fusion
   877          fi
   878          echo
   879          echo "${txtylw}Modified systemd service unit${txtrst}"
   880          echo
   881          echo "<<< ${txtgrn}✓${txtrst} Changed auto-start setting >>>"
   882          echo
   883          pauseScript
   884      fi
   885  }
   886  
   887  change_explorer() {
   888      local nodename="$(getCfgValue 'nodeName')"
   889      if [ -n "$nodename" ]; then
   890          echo
   891          echo "The node is currently listed on the node explorer as ${txtgrn}$nodename${txtrst}"
   892      else
   893          echo
   894          echo "The node is currently not listed on the node explorer"
   895      fi
   896      local question="${txtylw}Do you want to change this setting? Doing so will enforce a node update and restart!${txtrst} [Y/n] "
   897      askToContinue "$question"
   898      if [ $? -eq 0 ]; then
   899          echo
   900          echo "<<< Changing explorer listing >>>"
   901          updateExplorerListing
   902          updateNode
   903          echo
   904          echo "<<< ${txtgrn}✓${txtrst} Changed explorer listing >>>"
   905          echo
   906          pauseScript
   907      fi
   908  }
   909  
   910  warnRetreat() {
   911      local nodetype="$(getCfgValue 'nodeType')"
   912      if [ "$nodetype" != "minerandlocalgateway" -a "$nodetype" != "efsn" ]; then
   913          return
   914      fi
   915      local mining="$(getCfgValue 'mining')"
   916      if [ "$mining" = "false" ]; then
   917          return
   918      fi
   919      local address="$(getCfgValue 'address')"
   920      if [ -z "$address" ]; then
   921          return
   922      fi
   923  
   924      local tickets=$(sudo docker exec fusion efsn --exec "fsn.totalNumberOfTicketsByAddress(\"$address\")" attach /fusion-node/data/efsn.ipc 2>/dev/null)
   925  
   926      if [ -n "$tickets" ] && [ $tickets -gt 0 ]; then
   927          echo
   928          echo "${txtylw}Your node is currently configured for mining and has $tickets active tickets.${txtrst}"
   929          echo "If you stop the node for too long, your tickets might get retreated."
   930          echo
   931          local question="${txtylw}Are you sure you want to continue?${txtrst} [Y/n] "
   932          askToContinue "$question"
   933          [ $? -eq 0 ] || return 1
   934      fi
   935  }
   936  
   937  configureNode() {
   938      while true; do
   939          [ $DEBUG_MODE -ne 1 ] && clear
   940          echo
   941          echo "----------------------"
   942          echo "| Node Configuration |"
   943          echo "----------------------"
   944          echo
   945          echo "${txtylw}1. Enable/disable ticket auto-buy"
   946          echo "2. Enable/disable mining of new blocks"
   947          echo "3. Change staking wallet to be unlocked"
   948          echo "4. Enable/disable auto-start after boot"
   949          echo "5. Hide/show or rename on node explorer"
   950          echo "6. Return to main menu${txtrst}"
   951          echo
   952          local input
   953          read -n1 -r -s -p "Select option [1-6] " input
   954          case $input in
   955              1) echo; change_autobuy ;;
   956              2) echo; warnRetreat && change_mining ;;
   957              3) echo; warnRetreat && change_wallet ;;
   958              4) echo; change_autostart ;;
   959              5) echo; change_explorer ;;
   960              6) break ;;
   961              *) echo -e "\n${txtred}Invalid input${txtrst}"; sleep 1 ;;
   962          esac
   963      done
   964  }
   965  
   966  show_menus_init() {
   967      [ $DEBUG_MODE -ne 1 ] && clear
   968      echo
   969      echo "-----------------------"
   970      echo "| FUSION Node Manager |"
   971      echo "-----------------------"
   972      echo
   973      echo "${txtylw}1. Install node and dependencies"
   974      echo "2. Exit to shell${txtrst}"
   975      echo
   976  }
   977  
   978  read_options_init() {
   979      local input
   980      read -n1 -r -s -p "Select option [1-2] " input
   981      case $input in
   982          1) installNode ;;
   983          2) echo -e "\n${txtylw}Bye...${txtrst}"; exit 0 ;;
   984          *) echo -e "\n${txtred}Invalid input${txtrst}"; sleep 1 ;;
   985      esac
   986  }
   987  
   988  show_menus() {
   989      [ $DEBUG_MODE -ne 1 ] && clear
   990      echo
   991      echo "-----------------------"
   992      echo "| FUSION Node Manager |"
   993      echo "-----------------------"
   994  
   995      if [ $hasUpdate -eq 0 ]; then
   996          echo
   997          echo "${txtgrn}An update is available, please update your node!${txtrst}"
   998      fi
   999  
  1000      echo
  1001      echo "${txtylw}1. Install node and dependencies"
  1002      echo "2. Update node to current version"
  1003      echo "3. Start the node"
  1004      echo "4. Stop the node"
  1005      echo "5. Deinstall node"
  1006      echo "6. Show node logs"
  1007      echo "7. Configure node"
  1008      echo "8. Exit to shell${txtrst}"
  1009      echo
  1010  }
  1011  
  1012  read_options() {
  1013      local input
  1014      read -n1 -r -s -p "Select option [1-8] " input
  1015      case $input in
  1016          1) echo; warnRetreat && installNode ;;
  1017          2) updateNodeScreen ;;
  1018          3) echo; startNode; echo; pauseScript ;;
  1019          4) echo; warnRetreat && stopNode && echo && pauseScript ;;
  1020          5) echo; warnRetreat && deinstallNode ;;
  1021          6) showNodeLogs ;;
  1022          7) configureNode ;;
  1023          8) echo -e "\n${txtylw}Bye...${txtrst}"; exit 0 ;;
  1024          *) echo -e "\n${txtred}Invalid input${txtrst}"; sleep 1 ;;
  1025      esac
  1026  }
  1027  
  1028  [ $DEBUG_MODE -ne 1 ] && clear
  1029  echo
  1030  echo "-----------------------"
  1031  echo "| FUSION Node Manager |"
  1032  echo "-----------------------"
  1033  echo
  1034  echo "${txtylw}Initializing script, please wait...${txtrst}"
  1035  echo
  1036  # make sure we're not running into avoidable problems during setup
  1037  scriptUpdate
  1038  distroChecks
  1039  # check dependencies instead of install dependencies
  1040  # because the install method may diff in differrent OS
  1041  dependChecks
  1042  sanityChecks
  1043  # check for updates if node.json already exists, save state in global variable
  1044  hasUpdate=1
  1045  if [ -f "$CONF_FILE" ]; then
  1046      checkUpdate
  1047      hasUpdate=$?
  1048  fi
  1049  
  1050  # ignoring some signals to keep the script running
  1051  trap '' SIGINT SIGQUIT SIGTSTP
  1052  while true; do
  1053      # showing only the install option on initial launch
  1054      if [ ! -f "$CONF_FILE" ]; then
  1055          show_menus_init
  1056          read_options_init
  1057      else
  1058          show_menus
  1059          read_options
  1060      fi
  1061  done
  1062  
  1063  #/* vim: set ts=4 sts=4 sw=4 et : */