#!/usr/local/bin/bash

# Copyright (c) Inform Systems Group LLC by Alexey Ignatev, www.isg.dev
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.

# Update Polkadot version 1.6.11

SRC_DIR="/usr/local/polkadot-sdk"
LOG="/var/log/update_dot.log"
SERVICE_NAME="polkadot" # name for command: service $SERV_NAME stop and start
NEWSERVICE_DIR="${SRC_DIR}/target/release"
OLDSERVICE_DIR="/usr/local/bin"
STOP_TIMEOUT="300"
RUST_UPDATE="1"
GIT_CHECKOUT="1"
CHECK_ACTIVE_VALIDATION="0"
DOT_DIR="/root/.local/share/polkadot"
POLKADOT_BINS="polkadot polkadot-execute-worker polkadot-prepare-worker polkadot-omni-node polkadot-parachain substrate-node"

send_log() {
    echo `date "+%Y.%m.%d %H:%M:%S"` ${1} >>${LOG}
    echo `date "+%Y.%m.%d %H:%M:%S"` ${1}
}

if [ -f /etc/rc.conf ]; then
  . /etc/rc.conf
else
    send_log "Including rc.conf error! Exiting..."
    exit 1
fi

if [ ! "${polkadot_update}" = "Yes" ]; then
    send_log "Polkadot update disabled. Exiting..."
    exit 1
fi

if [ ! -n "${polkadot_addr}" ]; then
    send_log "Variable polkadot_addr not found in rc.conf! Exiting..."
    exit 1
fi


if [ ! -n "${polkadot_chain}" ]; then
    send_log "Variable polkadot_chain not found in rc.conf! Exiting..."
    exit 1
fi

NODE_ADDR=${polkadot_addr}
GET_DATA_LINK="https://api.metaspan.io/api/${polkadot_chain}/candidate"

# block for fixing polkadot compile problem on any systems
MAP_FIX="0"
MAP_RUSTIX_DIR="/usr/local/polkadot_support/rustix-0.33.7"

# HOW TO FIX MAP_FIXED POLKADOT COMPILE PROBLEM:
# 1. Grab the source code for the node.
# 2. Download this library and unpack it somewhere: https://github.com/bytecodealliance/rustix/archive/refs/tags/v0.33.7.zip
# 3. Open rustix-0.33.7/src/imp/libc/io/types.rs in an editor and find this piece of code:
#        /// `MAP_FIXED`
#        #[cfg(not(any(
#            target_os = "android",
#            target_os = "emscripten",
#            target_os = "freebsd",
#            target_os = "fuchsia",
#            target_os = "openbsd",
#            target_os = "redox",
#        )))]
#        const FIXED = c::MAP_FIXED;
# 4. Remove the line which says target_os = "freebsd"


if [ -d "${SRC_DIR}" ]; then
    cd ${SRC_DIR}
else 
    send_log "Directory ${SRC_DIR} with sources NOT FOUND! Exiting"
    exit 1
fi

if ! which ${SERVICE_NAME}; then
    send_log "Service binary: ${SERVICE_BIN} NOT FOUND! Exiting"
    exit 1
fi

if [ ${polkadot_mode} = "validator" ] && [ ${CHECK_ACTIVE_VALIDATION} = "1" ]; then
    send_log "Node in validation mode, checking node status..."
    STATUS_ACT="`/usr/local/bin/curl ${GET_DATA_LINK}/${NODE_ADDR} | /usr/local/bin/jq '.active'`"
    if [ ! "${STATUS_ACT}" = "0" ] && [ ! "$1" = "force" ]; then
	send_log "Node status (${GET_DATA_LINK}/${NODE_ADDR}) not a inactive (${STATUS_ACT}).. skipping update"
	exit 1
    fi
else
    send_log "Skipping node activity check..."
fi    

#updaing sources
send_log "Stating ${SERVICE_NAME} automatic update script. Updating source code..."

git fetch >>${LOG} 2>&1
if [  "$?" = "0" ]; then
     send_log "Updating sources completed"
else
     send_log "Updating ${SERVICE_NAME} sources ERROR! Exiting"
     exit 1
fi

#cheking version
BIN_VER="`${OLDSERVICE_DIR}/${SERVICE_NAME} -V | grep -oE '[0-9]+\.[0-9]+\.[0-9]+'`"
SRC_VER="`git tag -l | sort -V | grep "polkadot-" | grep -v -- '-rc' | grep -v -- '-beta' |tail -1 | awk -F"v" 'BEGIN{OFS="v";} {print $2;}'`"

if [ ! "$1" = "force" ]; then
    if [ "${BIN_VER}" = "${SRC_VER}" ]; then
	send_log "Binary version: ${BIN_VER}, source version is ${SRC_VER}. Version is same, skipping update"
	exit 0
    elif [ "$(printf "%s\n%s" "${BIN_VER}" "${SRC_VER}" | sort -V | head -n1)" = "${SRC_VER}" ]; then
	send_log "Binary version: ${BIN_VER}, source version is ${SRC_VER}. Unknown situation, update skipping! Exiting"
	exit 1
    fi
fi


# updating 
send_log "Binary version: ${BIN_VER}, source version is ${SRC_VER}. Starting update procedure"

if [ "${GIT_CHECKOUT}" = "1" ]; then
    git checkout -f "polkadot-v"${SRC_VER} >>${LOG} 2>&1
    if [ "$?" = "0" ]; then
	send_log "Git checkout to version ${SRC_VER} successful"
    else
	send_log "Checkout to version ${SRC_VER} ERROR! Exiting"
	exit 1
    fi
fi

if [ ${MAP_FIX} = "1" ]; then
    send_log "Fixing MAP_FIXED compile..."
    echo "
[patch.crates-io]
rustix = { path = '${MAP_RUSTIX_DIR}' }" >>./Cargo.toml
fi

if [ "${RUST_UPDATE}" = "1" ]; then
    source $HOME/.cargo/env >>${LOG} 2>&1
    cargo update >>${LOG} 2>&1
    if [ "$?" = "0" ]; then
    	send_log "Cargo update successful"
    else
        send_log "Cargo update ERROR! Exiting"
        exit 1
    fi
    rustup update >>${LOG} 2>&1
    if [ "$?" = "0" ]; then
        send_log "Rust update successful"
    else
        send_log "Rust update ERROR! Exiting"
        exit 1
    fi
fi

nice -n 20 cargo build --release >>${LOG} 2>&1
if [ "$?" = "0" ]; then
    send_log "Sources build ${SERVICE_NAME} successfull complete"
else
    wait
    sleep 5
    send_log "Compiling ${SERVICE_NAME} binary ERROR! Removing sources and rebuild"
    cd /usr/local
    rm -rf ${SRC_DIR}
    wait
    git clone https://github.com/paritytech/polkadot-sdk >>${LOG} 2>&1
    if [  "$?" = "0" ]; then
	 send_log "Updating sources completed"
    else
	 send_log "Updating ${SERVICE_NAME} sources ERROR! Exiting"
        exit 1
    fi
    cd ${SRC_DIR}
    if [ "${GIT_CHECKOUT}" = "1" ]; then
	git checkout -f "polkadot-v"${SRC_VER} >>${LOG} 2>&1
	if [ "$?" = "0" ]; then
    	    send_log "Git checkout to version ${SRC_VER} successful"
	else
    	    send_log "Checkout to version ${SRC_VER} ERROR! Exiting"
    	    exit 1
	fi
    fi
    rm -rf /root/.cargo
    rm -rf /root/.rustup
    curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
    source $HOME/.cargo/env
    rustup update
    cargo update
#    rustup target add wasm32-unknown-unknown
    rustup target add wasm32v1-none --toolchain stable-x86_64-unknown-freebsd
    rustup component add rust-src
    rustup install nightly
    cargo build --release >>${LOG} 2>&1
    if [ ! "$?" = "0" ]; then
	send_log "Compiling ${SERVICE_NAME} binary ERROR! Exiting"
        exit 1
    fi
fi

NEWBIN_VER="`${NEWSERVICE_DIR}/${SERVICE_NAME} -V | grep -oE '[0-9]+\.[0-9]+\.[0-9]+'`"
if [ ! "${NEWBIN_VER}" = "${SRC_VER}" ]; then
    send_log "NEW ${SERVICE_NAME} binary version: ${NEWBIN_VER}, and SOURCE version: ${SRC_VER} mismach! Exiting"
    exit 1
elif [ "$(printf "%s\n%s" "${NEWBIN_VER}" "${BIN_VER}" | sort -V | head -n1)" = "${NEWBIN_VER}" ]; then
    send_log "NEW ${SERVICE_NAME} binary version: ${NEWBIN_VER}, running binary version: ${BIN_VER}. Unknown error! Exiting"
    exit 1
fi


# stopping running version
DOT_PID="`cat /var/run/${SERVICE_NAME}.pid`"

send_log "Stopping polkadot service with PID: ${DOT_PID}"
service ${SERVICE_NAME} stop >>${LOG} 2>&1

STOP_COUNT="0"
send_log "Start waiting for pid "${DOT_PID}" successful stopped (timeout seconds: ${STOP_TIMEOUT})"
while ps -A | grep "${DOT_PID}" |grep -v "grep ${DOT_PID}"; do
    if [ "${STOP_COUNT}" -gt "${STOP_TIMEOUT}" ]; then
        send_log "Stopping ${SERVICE_NAME} TIMEOUT! Exiting..."
        exit 1
    else
        send_log "Waiting for ${SERVICE_NAME} successful stopped... (${STOP_COUNT} seconds...)"
        sleep 1
    fi
    STOP_COUNT=$((${STOP_COUNT} + 1 ))
done

sleep 5

# backup
for BIN in ${POLKADOT_BINS}; do
    if [ -x "${OLDSERVICE_DIR}/${BIN}" ]; then
        send_log "Backing up old binary: ${OLDSERVICE_DIR}/${BIN}"
        mv -f ${OLDSERVICE_DIR}/${BIN} ${OLDSERVICE_DIR}/${BIN}.old >>${LOG} 2>&1
    else
        send_log "Binary:${OLDSERVICE_DIR}/${BIN} NOT FOUND! Exiting"
        exit 1
    fi
done

# replace
for BIN in ${POLKADOT_BINS}; do
    if [ -x "${NEWSERVICE_DIR}/${BIN}" ]; then
        send_log "Stripping and copying new binary: ${NEWSERVICE_DIR}/${BIN}"
        strip ${NEWSERVICE_DIR}/${BIN}
        cp -f ${NEWSERVICE_DIR}/${BIN} ${OLDSERVICE_DIR}/${BIN} >>${LOG} 2>&1
    else
        send_log "Binary:${NEWSERVICE_DIR}/${BIN} NOT FOUND! Exiting"
        exit 1
    fi
done

service ${SERVICE_NAME} start >>${LOG} 2>&1
if [ "$?" = "0" ]; then
    send_log "Starting new ${SERVICE_NAME} binary successful, update complete"
else
    send_log "Starting new ${SERVICE_NAME} binary ERROR! Exiting"
fi
