#!/bin/bash set -Ee CHECK_KEYS=false INSERT_KEYS=false UNIT_FILE=false SKIP_BUILD=false SET_ENVIRONMENT=false EXECUTABLE_PATH="/usr/bin/" SPECIFICATION_PATH="/etc/ghost/" NODE_PATH="/var/lib/ghost" SPECIFICATION_NAME="casper" TARGET="release" CURRENT_PATH=$(pwd) CURRENT_SCRIPT=$(realpath "$0") SCRIPT_FOLDER=$(dirname "$CURRENT_SCRIPT") PROJECT_FOLDER=("$SCRIPT_FOLDER/..") final() { cd $CURRENT_PATH if [[ $1 -eq 1 ]]; then echo "[-] error occured during execution" else echo "[+] execution finished" fi exit $1 } prompt() { while true; do printf "$1 [y/N]: " read yn case $yn in [Yy]* ) return 0;; * ) return 1;; esac done } sanity_check() { secret_seed=$(ghost key inspect --scheme="$1" "$2" | grep "Secret seed" | awk '{ print $3 }') account_id=$(ghost key inspect --scheme="$1" "$2" | grep "Account ID" | awk '{ print $3 }') echo "[+] inspected account id for $3: $account_id" num_keys=$(grep $account_id "$PROJECT_FOLDER/service/ghosties" | wc -l) num_types=$(grep $account_id "$PROJECT_FOLDER/service/ghosties" | grep $3 | wc -l) if [ $num_keys = 1 ] && [ $num_types = 1 ]; then echo "[+] local $3 key found in 'ghosties' with correct key type" else echo "[-] inspected account id not found on 'ghosties' file or wrong key type" exit 1 fi } genesis_check() { secret_seed=$(ghost key inspect --scheme="$1" "$2" | grep "Secret seed" | awk '{ print $3 }') account_id=$(ghost key inspect --scheme="$1" "$2" | grep "Account ID" | awk '{ print $3 }') num_keys=$(grep ${account_id:2} "$PROJECT_FOLDER/service/chain-specs/casper.json" | wc -l) if [ ! $num_keys = 0 ]; then echo "[+] found in genesis block" else echo "[-] not yet in genesis block" fi echo } extract_seed() { name_with_spaces=$(echo $1 | tr '-' ' ') read -p "[?] path to the file with $name_with_spaces: (default: /etc/ghost/$1) " seed_path seed_path="${seed_path:-/etc/ghost/$1}" if [ ! -f $seed_path ]; then echo "[-] path to $name_with_spaces is not valid" fi seed=$(cat $seed_path) echo $seed } help() { echo -e "Ghost Node Build automation tool. Helper for Ghost Node environment preparation.\n" echo -e "With no OPTION nothing will happen, possible OPTION:\n" echo -e "-i, --set-environment\n\tSet up rust environment." echo -e "-u, --unit-file\n\tCreation of systemd unit file." echo -e "-m, --make-global\n\tStore compiled ghost executable and chain specification globally." echo -e "-a, --set-arguments\n\tPrepare CLI arguments for running ghost node." echo -e "-k, --check-keys\n\tCheck if your keys are already included in 'ghosties' file." echo -e "-r, --insert-keys\n\tInsert session keys to the keystore via JSON RPC." echo -e "-r, --release\n\tCompile node with '--release' flag." echo -e "-p, --profile\n\tCompile node with '--profile [PROFILE]' flag." echo -e "-f, --features\n\tCompilation features '--features=\"FEATURE1,FEATURE2\"'" echo -e "-e, --executable-path\n\tPath to executable ('/usr/lib/' is default)." echo -e "-c, --specification-path\n\tPath to specification ('/etc/ghost' is default)." echo -e "-n, --specification-name\n\tSpecification name to be used ('casper' is default)." echo -e "-h, --help\n\tPrints help information." } trap 'final "$?"' EXIT clear echo " ____ _ _ _ _ _" echo " / ___| |__ ___ ___| |_ | \ | | ___ __| | ___" echo "| | _| '_ \ / _ \/ __| __| | \| |/ _ \ / _' |/ _ \\" echo "| |_| | | | | (_) \__ \ |_ | |\ | (_) | (_| | __/" echo " \____|_| |_|\___/|___/\__| |_| \_|\___/ \__,_|\___|" echo -e "\nCreated by st1nky (stinky@ghostchain.io)" echo -e "Repository: https://git.ghostchain.io/ghostchain/ghost-node" echo -e "Usage: local-builder.sh [OPTION]\n" while [ $# -gt 0 ]; do case "$1" in --set-environment|-i) SET_ENVIRONMENT=true ;; --unit-file|-u) UNIT_FILE=true ;; --make-global|-m) MAKE_GLOBAL=true ;; --set-arguments|-a) ARGUMENTS=true ;; --check-keys|-k) CHECK_KEYS=true ;; --insert-keys|-k) INSERT_KEYS=true ;; --release|-r) RELEASE="--release" TARGET="release" ;; --profile*|-p*) if [[ "$1" != *=* ]]; then shift; fi RELEASE="--profile=${1#*=}" TARGET="${1#*=}" ;; --feature*|-f*) if [[ "$1" != *=* ]]; then shift; fi FEATURES="--features=${1#*=}" ;; --executable-path*|-e*) if [[ "$1" != *=* ]]; then shift; fi EXECUTABLE_PATH=$(echo ${1#*=}/ | tr -s /) ;; --specification-path*|-c*) if [[ "$1" != *=* ]]; then shift; fi SPECIFICATION_PATH=$(echo ${1#*=}/ | tr -s /) ;; --specification-name*|-n*) if [[ "$1" != *=* ]]; then shift; fi SPECIFICATION_NAME="${1#*=}" ;; --help|-h) help exit 0 ;; *) help exit 1 ;; esac shift done if [[ $SET_ENVIRONMENT = true ]]; then echo -e "\n" echo "WARNING!!! THIS IS HIGHLY EXPERIMENTAL FLAG, USE IT ONLY ON YOUR" echo "OWN RISK! NOT EVERY SYSTEM CHECKED AND PROVED TO BE WORKING AS" echo "EXPECTED! FOR THOSE WHO FOUND BUG OR SOME INCONSISTENCY FEEL" echo "FREE TO FILL ISSUE ON https://git.ghostchain.io/ghostchain/ghost-node/issues" echo -e "\n" if prompt "[?] are you brave enough?"; then echo "[+] you were warned, I hope you know what you're doing" else echo "[-] aborting environment setup" exit 1 fi if command -v rustc >/dev/null 2>&1; then echo "[+] rust already installed" else os_name=$(uname -s) cd $HOME if [ "$os_name" = "Darwin" ]; then echo "[+] detected MacOS. installing dependencies via homebrew" if command -v brew >/dev/null 2>&1; then echo "[+] already installed: $(brew --version)" else if prompt "[?] do you want to install homebrew?"; then /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)" else echo "[-] cannot continue without homebrew" exit 1 fi fi brew update if prompt "[?] install git, cmake, openssl and protobuf?"; then brew install git cmake openssl protobuf else echo "[!] assuming git, cmake, openssl and protobuf are present" fi elif [ "$os_name" == "Linux" ]; then distro=$(cat /etc/*-release | tr '[:upper:]' '[:lower:]' | grep -Poi '(debian|ubuntu|arch|fedora|opensuse)' | uniq | head -n 1) echo $distro if [ "$distro" = "ubuntu" ]; then echo "[+] ubuntu detected, using apt to install dependencies" sudo apt update sudo apt install --assume-yes git make clang curl libssl-dev protobuf-compiler elif [ "$distro" = "debian" ]; then echo "[+] debian detected, using apt to install dependencies" sudo apt update sudo apt install --assume-yes git make clang curl libssl-dev llvm libudev-dev protobuf-compiler elif [ "$distro" = "arch" ]; then echo "[+] arch linux detected, using pacman to install dependencies" sudo pacman -Syu --needed --no-confirm curl git clang make protobuf elif [ "$distro" = "fedora" ]; then echo "[+] fedora detected, using dnf to install dependencies" sudo dnf update --assumeyes sudo dnf install --assumeyes clang curl git openssl-devel make protobuf-compiler perl elif [ "$distro" = "opensuse" ]; then echo "[+] openSUSE detected, using zypper to install dependencies" sudo zypper install --no-confirm clang gcc gcc-c++ curl git openssl-devel llvm-devel libudev-devel make awk protobuf-devel else if prompt "[?] unknown linux distribution, unable to install dependencies. continue anyway?"; then echo "[+] proceeding with unknown linux distribution" else echo "[-] aborting with uknown distribution" exit 1 fi fi else echo "[-] unknown operating system" exit 1 fi fi if prompt "[?] setup the rust environment (e.g. WASM support)?"; then rustup default stable rustup update rustup target add wasm32-unknown-unknown rustup component add rust-src fi fi if [[ ! -z $RELEASE ]]; then if prompt "[?] 'cargo build $RELEASE $FEATURES' is what you want?"; then cd $PROJECT_FOLDER echo "[+] Starting build in 3 seconds..." sleep 3 cargo build $RELEASE $FEATURES fi fi if [[ $MAKE_GLOBAL = true ]]; then cd $PROJECT_FOLDER sudo cp target/$TARGET/ghost $EXECUTABLE_PATH cp service/chain-specs/$SPECIFICATION_NAME.json $SPECIFICATION_PATH echo "[+] ghost executable copied in '$EXECUTABLE_PATH' from '$TARGET'" echo "[+] specification '$SPECIFICATION_NAME.json' copied to '$SPECIFICATION_PATH'" fi if [ $UNIT_FILE = true ]; then cd $SCRIPT_FOLDER read -p "[?] name for the unit file (default: ghost-node.service) " unit_name if [ -z $unit_name ]; then unit_name="ghost-node" fi unit_name=$(echo "$unit_name" | sed -e "s/.service//g") unit_name="$unit_name.service" user_name="ghost" if id $user_name > /dev/null 2>&1; then echo "[+] user ghost found, continue for the user ghost" else echo "[!] user ghost not found" if prompt "[?] do you want to create ghost user? (NOT RECOMMENDED: current $(whoami))"; then sudo useradd --system --create-home $user_name if [ ! -d $NODE_PATH ]; then echo "[+] create folder for the node at '$NODE_PATH'" sudo mkdir $NODE_PATH fi else user_name=$(whoami) fi sudo chown $user_name $NODE_PATH echo "[+] write permission to '$NODE_PATH'" fi cp packaging/template.service /tmp/$unit_name sed -i -e "s/User=ghost/User=$user_name/g" /tmp/$unit_name sudo cp packaging/template.service /etc/systemd/system/$unit_name sudo systemctl daemon-reload if prompt "[?] do you want to start the $unit_name?"; then sudo systemctl restart $unit_name fi if prompt "[?] do you want to enable the $unit_name?"; then sudo systemctl enable $unit_name fi fi if [[ $ARGUMENTS = true ]]; then echo "[+] setting-up default CLI arguments" CLI_ARGS=() read -p "[?] specify p2p protocol TCP port (default: 30333): " port CLI_ARGS+=("--port=${port:-30333}") read -p "[?] specify JSON-RPC server TCP port: (default: 9945): " rpc_port CLI_ARGS+=("--rpc-port=${rpc_port:-9945}") read -p "[?] specify the chain specification (default: /etc/ghost/casper.json): " chain CLI_ARGS+=("--chain=${chain:-/etc/ghost/casper.json}") read -p "[?] specify node's secret key file for p2p networking (default: /etc/ghost/node-key): " node_key CLI_ARGS+=("--node-key-file=${node_key:-/etc/ghost/node-key}") read -p "[?] specify name for the node (default: RANDOM_NAME): " node_name if [[ ! -z $node_name ]]; then CLI_ARGS+=("--name='$node_name'") fi CLI_ARGS+=("--validator") if prompt "[?] disable validator mode?"; then unset CLI_ARGS[-1] fi if prompt "[?] enable prometheus?"; then read -p "[?] specify prometheus exporter TCP port: (default: 9615)" prometheus_port CLI_ARGS+=("--prometheus-port=${prometheus_port:-9615}") else CLI_ARGS+=("--no-prometheus") fi read -p "[?] list of bootnodes if any: " bootnodes if [ ! -z $bootnodes ]; then CLI_ARGS+=("--bootnodes=$bootnodes") fi # default for now CLI_ARGS+=("--base-path=$NODE_PATH") CLI_ARGS+=("--state-pruning=archive") CLI_ARGS+=("--blocks-pruning=archive") CLI_ARGS+=("--rpc-methods=auto") CLI_ARGS+=("--no-telemetry") CLI_ARGS+=("--no-private-ip") CLI_ARGS+=("--no-mdns") CLI_ARGS+=("--no-hardware-benchmarks") echo "GHOST_CLI_ARGS=\"$(IFS=' '; echo "${CLI_ARGS[*]}")\"" > /tmp/ghost sudo cp /tmp/ghost /etc/default/ghost rm /tmp/ghost echo "[+] new CLI arguments stored in '/etc/default/ghost'" cat /etc/default/ghost fi if [ $CHECK_KEYS = true ]; then seed=$(extract_seed "wallet-key") sanity_check "sr25519" $seed "wallet" genesis_check "sr25519" $seed "wallet" seed=$(extract_seed "stash-key") sanity_check "sr25519" $seed "stash" genesis_check "sr25519" $seed "stash" seed=$(extract_seed "session-key") if [ $INSERT_KEYS = true ]; then read -p "[?] JSON RPC endpoint to the node: (default: localhost:9945) " rpc_endpoint rpc_endpoint="${rpc_endpoint:-localhost:9945}" fi for type in $(echo audi babe gran slow); do echo "[+] parsing session key for [$type]" scheme="sr25519" if [ $type = "gran" ]; then scheme="ed25519" fi sanity_check $scheme "$seed//$type" $type genesis_check $scheme "$seed//$type" $type if [ $INSERT_KEYS = true ]; then echo "[+] trying to make an 'author_insertKey' RPC call to $rpc_endpoint..." curl --location $rpc_endpoint \ --header "Content-Type: application/json" \ --data '{ "id":1, "jsonrpc":"2.0", "method":"author_insertKey", "params": ["'"$type"'", "'"$secret_seed"'", "'"$account_id"'"] }' fi echo done fi