diff --git a/scripts/cnode-helper-scripts/mithril-client.sh b/scripts/cnode-helper-scripts/mithril-client.sh index 54c9a140c..4787e4baa 100755 --- a/scripts/cnode-helper-scripts/mithril-client.sh +++ b/scripts/cnode-helper-scripts/mithril-client.sh @@ -2,7 +2,7 @@ # shellcheck disable=SC2086 #shellcheck source=/dev/null -. "$(dirname $0)"/env offline +. "$(dirname $0)"/mithril.library ###################################### # User Variables - Change as desired # @@ -15,9 +15,6 @@ # Do NOT modify code below # ###################################### -U_ID=$(id -u) -G_ID=$(id -g) - ##################### # Functions # ##################### @@ -25,8 +22,8 @@ G_ID=$(id -g) usage() { cat <<-EOF - Usage: $(basename "$0") - Script to run Cardano Mithril Client + Usage: $(basename "$0") [-u] [] + A script to run Cardano Mithril Client -u Skip script update check overriding UPDATE_CHECK value in env (must be first argument to script) @@ -50,62 +47,10 @@ EOF } SKIP_UPDATE=N -[[ $1 = "-u" ]] && SKIP_UPDATE=Y && shift +[[ $1 = "-u" ]] && export SKIP_UPDATE=Y && shift ## mithril environment subcommands -environment_setup() { - local env_file="${CNODE_HOME}/mithril/mithril.env" - - if [[ -f "$env_file" ]]; then - if [[ "$UPDATE_ENVIRONMENT" != "Y" ]]; then - echo "Error: $env_file already exists. To update it, set UPDATE_ENVIRONMENT to 'Y'." >&2 - return 1 - else - echo "Updating $env_file..." - fi - else - echo "Creating $env_file..." - fi - - if [[ ! -d "${CNODE_HOME}/mithril/data-stores" ]]; then - sudo mkdir -p "${CNODE_HOME}"/mithril/data-stores - sudo chown -R "$U_ID":"$G_ID" "${CNODE_HOME}"/mithril 2>/dev/null - fi - if [[ -n "${POOL_NAME}" ]] && [[ "${POOL_NAME}" != "CHANGE_ME" ]]; then - export ERA_READER_ADDRESS=https://raw.githubusercontent.com/input-output-hk/mithril/main/mithril-infra/configuration/${RELEASE}-${NETWORK_NAME,,}/era.addr - export ERA_READER_VKEY=https://raw.githubusercontent.com/input-output-hk/mithril/main/mithril-infra/configuration/${RELEASE}-${NETWORK_NAME,,}/era.vkey - bash -c "cat <<-'EOF' > ${CNODE_HOME}/mithril/mithril.env - KES_SECRET_KEY_PATH=${POOL_DIR}/${POOL_HOTKEY_SK_FILENAME} - OPERATIONAL_CERTIFICATE_PATH=${POOL_DIR}/${POOL_OPCERT_FILENAME} - NETWORK=${NETWORK_NAME,,} - RELEASE=${RELEASE} - AGGREGATOR_ENDPOINT=https://aggregator.${RELEASE}-${NETWORK_NAME,,}.api.mithril.network/aggregator - RUN_INTERVAL=60000 - DB_DIRECTORY=${CNODE_HOME}/db - CARDANO_NODE_SOCKET_PATH=${CARDANO_NODE_SOCKET_PATH} - CARDANO_CLI_PATH=${HOME}/.local/bin/cardano-cli - DATA_STORES_DIRECTORY=${CNODE_HOME}/mithril/data-stores - STORE_RETENTION_LIMITS=5 - ERA_READER_ADAPTER_TYPE=cardano-chain - ERA_READER_ADAPTER_PARAMS=$(jq -nc --arg address "$(wget -q -O - "${ERA_READER_ADDRESS}")" --arg verification_key "$(wget -q -O - "${ERA_READER_VKEY}")" '{"address": $address, "verification_key": $verification_key}') - GENESIS_VERIFICATION_KEY=$(wget -q -O - https://raw.githubusercontent.com/input-output-hk/mithril/main/mithril-infra/configuration/${RELEASE}-${NETWORK_NAME,,}/genesis.vkey) - PARTY_ID=$(cat ${POOL_DIR}/${POOL_ID_FILENAME}) - SNAPSHOT_DIGEST=latest - EOF" - else - bash -c "cat <<-'EOF' > ${CNODE_HOME}/mithril/mithril.env - NETWORK=${NETWORK_NAME,,} - RELEASE=${RELEASE} - AGGREGATOR_ENDPOINT=https://aggregator.${RELEASE}-${NETWORK_NAME,,}.api.mithril.network/aggregator - DB_DIRECTORY=${CNODE_HOME}/db - GENESIS_VERIFICATION_KEY=$(wget -q -O - https://raw.githubusercontent.com/input-output-hk/mithril/main/mithril-infra/configuration/${RELEASE}-${NETWORK_NAME,,}/genesis.vkey) - SNAPSHOT_DIGEST=latest - EOF" - fi - chown $USER:$USER "${CNODE_HOME}"/mithril/mithril.env -} - environment_override() { local var_to_override="$1" local new_value="$2" @@ -121,96 +66,33 @@ environment_override() { sed -i "s|^${var_to_override}=.*|${var_to_override}=${new_value}|" "$env_file" } -pre_startup_sanity() { - if [[ ${UPDATE_CHECK} = Y && ${SKIP_UPDATE} != Y ]]; then - - echo "Checking for script updates..." - - # Check availability of checkUpdate function - if [[ ! $(command -v checkUpdate) ]]; then - echo -e "\nCould not find checkUpdate function in env, make sure you're using official guild docos for installation!" - exit 1 - fi - - # check for env update - ENV_UPDATED=${BATCH_AUTO_UPDATE} - checkUpdate "${PARENT}"/env N N N - case $? in - 1) ENV_UPDATED=Y ;; - 2) exit 1 ;; - esac - - # check for cncli.sh update - checkUpdate "${PARENT}"/cncli.sh ${ENV_UPDATED} - case $? in - 1) $0 "-u" "$@"; exit 0 ;; # re-launch script with same args skipping update check - 2) exit 1 ;; - esac - fi - +mithril_init() { REQUIRED_PARAMETERS="Y" - if [[ ! -f "${CNODE_HOME}"/mithril/mithril.env ]]; then - echo "INFO: Mithril environment file not found, creating environment file.." - environment_setup && echo "INFO: Mithril environment file created successfully!!" - elif [[ "${UPDATE_ENVIRONMENT}" == "Y" ]]; then - echo "INFO: Updating mithril environment file.." - environment_setup && echo "INFO: Mithril environment file updated successfully!!" - fi . "${CNODE_HOME}"/mithril/mithril.env - [[ -z "${NETWORK}" ]] && echo "ERROR: The NETWORK must be set before calling mithril-client!!" && REQUIRED_PARAMETERS="N" + [[ -z "${NETWORK}" ]] && echo "ERROR: The NETWORK must be set before calling $(basename "${0::-3}")!!" && REQUIRED_PARAMETERS="N" [[ -z "${RELEASE}" ]] && echo "ERROR: Failed to set RELEASE variable, please check NETWORK variable in env file!!" && REQUIRED_PARAMETERS="N" - [[ -z "${CNODE_HOME}" ]] && echo "ERROR: The CNODE_HOME must be set before calling mithril-client!!" && REQUIRED_PARAMETERS="N" + [[ -z "${CNODE_HOME}" ]] && echo "ERROR: The CNODE_HOME must be set before calling $(basename "${0::-3}")!!" && REQUIRED_PARAMETERS="N" [[ ! -d "${CNODE_HOME}" ]] && echo "ERROR: The CNODE_HOME directory does not exist, please check CNODE_HOME variable in env file!!" && REQUIRED_PARAMETERS="N" - [[ -z "${AGGREGATOR_ENDPOINT}" ]] && echo "ERROR: The AGGREGATOR_ENDPOINT must be set before calling mithril-client!!" && REQUIRED_PARAMETERS="N" - [[ -z "${GENESIS_VERIFICATION_KEY}" ]] && echo "ERROR: The GENESIS_VERIFICATION_KEY must be set before calling mithril-client!!" && REQUIRED_PARAMETERS="N" + [[ -z "${AGGREGATOR_ENDPOINT}" ]] && echo "ERROR: The AGGREGATOR_ENDPOINT must be set before calling $(basename "${0::-3}")!!" && REQUIRED_PARAMETERS="N" + [[ -z "${GENESIS_VERIFICATION_KEY}" ]] && echo "ERROR: The GENESIS_VERIFICATION_KEY must be set before calling $(basename "${0::-3}")!!" && REQUIRED_PARAMETERS="N" [[ ! -x "${MITHRILBIN}" ]] && echo "ERROR: The MITHRILBIN variable does not contain an executable file, please check MITHRILBIN variable in env file!!" && REQUIRED_PARAMETERS="N" [[ "${REQUIRED_PARAMETERS}" != "Y" ]] && exit 1 export GENESIS_VERIFICATION_KEY DOWNLOAD_SNAPSHOT="N" - REMOVE_DB_DIR="N" } -set_defaults() { - [[ -z "${MITHRILBIN}" ]] && MITHRILBIN="${HOME}"/.local/bin/mithril-client - if [[ -z "${NETWORK_NAME}" ]]; then - echo "ERROR: The NETWORK_NAME must be set before mithril-client can download snapshots!!" - exit 1 - else - case "${NETWORK_NAME,,}" in - mainnet|preprod|guild) - RELEASE="release" - ;; - preview) - RELEASE="pre-release" - ;; - *) - echo "ERROR: The NETWORK_NAME must be set to Mainnet, PreProd, Preview, Guild before mithril-client can download snapshots!!" - exit 1 - esac - fi - pre_startup_sanity -} check_db_dir() { # If the DB directory does not exist then set DOWNLOAD_SNAPSHOT to Y if [[ ! -d "${DB_DIRECTORY}" ]]; then echo "INFO: The db directory does not exist.." DOWNLOAD_SNAPSHOT="Y" - # If the DB directory is empty then set DOWNLOAD_SNAPSHOT to Y and REMOVE_DB_DIR to Y + # If the DB directory is empty then set DOWNLOAD_SNAPSHOT to Y elif [[ -d "${DB_DIRECTORY}" ]] && [[ -z "$(ls -A "${DB_DIRECTORY}")" ]] && [[ $(du -cs "${DB_DIRECTORY}"/* 2>/dev/null | awk '/total$/ {print $1}') -eq 0 ]]; then echo "INFO: The db directory is empty.." - REMOVE_DB_DIR="Y" DOWNLOAD_SNAPSHOT="Y" else - echo "INFO: The db directory is not empty.." - fi -} - -remove_db_dir() { - # Mithril client errors if the db folder already exists, so remove it if it is empty - if [[ "${REMOVE_DB_DIR}" == "Y" ]]; then - echo "INFO: Removing empty db directory to prepare for snapshot download.." - rmdir "${DB_DIRECTORY}" + echo "INFO: The db directory is not empty, skipping Cardano DB download.." fi } @@ -221,7 +103,7 @@ download_snapshot() { echo "INFO: Downloading latest mithril snapshot.." "${MITHRILBIN}" -v --aggregator-endpoint ${AGGREGATOR_ENDPOINT} snapshot download --download-dir ${CNODE_HOME} ${SNAPSHOT_DIGEST} else - echo "INFO: Skipping snapshot download.." + echo "INFO: Skipping Cardano DB download.." fi } @@ -265,31 +147,39 @@ download_stake_distribution() { } list_stake_distributions() { - if [[ $1 == "json" ]]; then - "${MITHRILBIN}" -v --aggregator-endpoint ${AGGREGATOR_ENDPOINT} mithril-stake-distribution list --json - else - "${MITHRILBIN}" -v --aggregator-endpoint ${AGGREGATOR_ENDPOINT} mithril-stake-distribution list - fi + local json_flag="" + + for arg in "$@"; do + if [[ $arg == "json" ]]; then + json_flag="--json" + fi + done + + "${MITHRILBIN}" -v --aggregator-endpoint ${AGGREGATOR_ENDPOINT} mithril-stake-distribution list $json_flag + } ##################### -# Execution # +# Execution/Main # ##################### +update_check "$@" + +set_defaults + # Parse command line options case $1 in environment) - set_defaults case $2 in setup) - environment_setup + generate_environment_file ;; override) environment_override $3 $4 ;; update) - UPDATE_ENVIRONMENT="Y" - environment_setup + export UPDATE_ENVIRONMENT="Y" + generate_environment_file ;; *) echo "Invalid environment subcommand: $2" >&2 @@ -299,11 +189,9 @@ case $1 in esac ;; snapshot) - set_defaults case $2 in download) check_db_dir - remove_db_dir download_snapshot ;; list) @@ -327,7 +215,6 @@ case $1 in esac ;; stake-distribution) - set_defaults case $2 in download) download_stake_distribution diff --git a/scripts/cnode-helper-scripts/mithril-relay.sh b/scripts/cnode-helper-scripts/mithril-relay.sh index 6c104fddf..a01088a23 100755 --- a/scripts/cnode-helper-scripts/mithril-relay.sh +++ b/scripts/cnode-helper-scripts/mithril-relay.sh @@ -1,4 +1,8 @@ #!/bin/bash +# shellcheck disable=SC2086 +#shellcheck source=/dev/null + +. "$(dirname $0)"/mithril.library ###################################### # User Variables - Change as desired # @@ -26,16 +30,18 @@ RELAY_LISTENING_IP=() usage() { cat <<-EOF - $(basename "$0") [-d] [-l] + $(basename "$0") [-d] [-l] [-u] [-h] + A script to setup Cardano Mithril relays - Cardano Mithril relay wrapper script!! -d Install squid and configure as a relay -l Install nginx and configure as a load balancer + -u Skip update check -h Show this help text EOF } + generate_nginx_conf() { sudo bash -c "cat > /etc/nginx/nginx.conf <<'EOF' worker_processes 1; @@ -123,76 +129,103 @@ generate_squid_conf() { EOF" } +deploy_nginx_load_balancer() { + # Install nginx and configure load balancing + echo -e "\nInstalling nginx load balancer" + sudo apt-get update + sudo apt-get install -y nginx + + # Read the listening IP addresses from user input + while true; do + read -r -p "Enter the IP address of a relay: " ip + RELAY_LISTENING_IP+=("${ip}") + read -r -p "Are there more relays? (y/n) " yn + case ${yn} in + [Nn]*) break ;; + *) continue ;; + esac + done + + # Read the listening IP for the load balancer + read -r -p "Enter the IP address of the load balancer (press Enter to use default 127.0.0.1): " SIDECAR_LISTENING_IP + SIDECAR_LISTENING_IP=${SIDECAR_LISTENING_IP:-127.0.0.1} + echo "Using IP address ${SIDECAR_LISTENING_IP} for the load balancer configuration." + + # Read the listening port from user input + read -r -p "Enter the relay's listening port (press Enter to use default 3132): " RELAY_LISTENING_PORT + RELAY_LISTENING_PORT=${RELAY_LISTENING_PORT:-3132} + echo "Using port ${RELAY_LISTENING_PORT} for relay's listening port." + + # Generate the nginx configuration file + generate_nginx_conf + # Restart nginx and check status + echo -e "\nStarting Mithril relay sidecar (nginx load balancer)" + sudo systemctl restart nginx + sudo systemctl status nginx + +} + +deploy_squid_proxy() { + # Install squid and make a backup of the config file + echo -e "\nInstalling squid proxy" + sudo apt-get update + sudo apt-get install -y squid + sudo cp /etc/squid/squid.conf /etc/squid/squid.conf.bak + + # Read the listening IP addresses from user input + while true; do + read -r -p "Enter the IP address of your Block Producer: " ip + BLOCK_PRODUCER_IP+=("${ip}") + read -r -p "Are there more block producers? (y/n) " yn + case ${yn} in + [Nn]*) break ;; + *) continue ;; + esac + done + + # Read the listening port from user input + read -r -p "Enter the relay's listening port (press Enter to use default 3132): " RELAY_LISTENING_PORT + RELAY_LISTENING_PORT=${RELAY_LISTENING_PORT:-3132} + echo "Using port ${RELAY_LISTENING_PORT} for relay's listening port." + generate_squid_conf + + # Restart squid and check status + echo -e "\nStarting Mithril relay (squid proxy)" + sudo systemctl restart squid + sudo systemctl status squid + + # Inform the user to create the appropriate firewall rule + for ip in "${RELAY_LISTENING_IP[@]}"; do + echo "Create the appropriate firewall rule: sudo ufw allow from ${ip} to any port ${RELAY_LISTENING_PORT} proto tcp" + done +} + +stop_relays() { + printf " Stopping squid proxy and nginx load balancers.." + sudo systemctl stop squid 2>/dev/null + sudo systemctl stop nginx 2>/dev/null + sleep 5 + exit 0 +} + +##################### +# Execution/Main # +##################### + # Parse command line arguments -while getopts :dlh opt; do +while getopts :dlsuh opt; do case ${opt} in d) - # Install squid and make a backup of the config file - echo -e "\nInstalling squid proxy" - sudo apt-get update - sudo apt-get install -y squid - sudo cp /etc/squid/squid.conf /etc/squid/squid.conf.bak - - # Read the listening IP addresses from user input - while true; do - read -r -p "Enter the IP address of your Block Producer: " ip - BLOCK_PRODUCER_IP+=("${ip}") - read -r -p "Are there more block producers? (y/n) " yn - case ${yn} in - [Nn]*) break ;; - *) continue ;; - esac - done - - # Read the listening port from user input - read -r -p "Enter the relay's listening port (press Enter to use default 3132): " RELAY_LISTENING_PORT - RELAY_LISTENING_PORT=${RELAY_LISTENING_PORT:-3132} - echo "Using port ${RELAY_LISTENING_PORT} for relay's listening port." - generate_squid_conf - - # Restart squid and check status - echo -e "\nStarting Mithril relay (squid proxy)" - sudo systemctl restart squid - sudo systemctl status squid - - # Inform the user to create the appropriate firewall rule - for ip in "${RELAY_LISTENING_IP[@]}"; do - echo "Create the appropriate firewall rule: sudo ufw allow from ${ip} to any port ${RELAY_LISTENING_PORT} proto tcp" - done + INSTALL_SQUID_PROXY=Y ;; l) - # Install nginx and configure load balancing - echo -e "\nInstalling nginx load balancer" - sudo apt-get update - sudo apt-get install -y nginx - - # Read the listening IP addresses from user input - while true; do - read -r -p "Enter the IP address of a relay: " ip - RELAY_LISTENING_IP+=("${ip}") - read -r -p "Are there more relays? (y/n) " yn - case ${yn} in - [Nn]*) break ;; - *) continue ;; - esac - done - - # Read the listening IP for the load balancer - read -r -p "Enter the IP address of the load balancer (press Enter to use default 127.0.0.1): " SIDECAR_LISTENING_IP - SIDECAR_LISTENING_IP=${SIDECAR_LISTENING_IP:-127.0.0.1} - echo "Using IP address ${SIDECAR_LISTENING_IP} for the load balancer configuration." - - # Read the listening port from user input - read -r -p "Enter the relay's listening port (press Enter to use default 3132): " RELAY_LISTENING_PORT - RELAY_LISTENING_PORT=${RELAY_LISTENING_PORT:-3132} - echo "Using port ${RELAY_LISTENING_PORT} for relay's listening port." - - # Generate the nginx configuration file - generate_nginx_conf - # Restart nginx and check status - echo -e "\nStarting Mithril relay sidecar (nginx load balancer)" - sudo systemctl restart nginx - sudo systemctl status nginx + INSTALL_NGINX_LOAD_BALANCER=Y + ;; + u) + export SKIP_UPDATE='Y' + ;; + s) + STOP_RELAYS=Y ;; h) usage @@ -220,3 +253,15 @@ if [[ ${OPTIND} -eq 1 ]]; then usage exit 1 fi + +[[ "${STOP_RELAYS}" == "Y" ]] && stop_relays + +update_check "$@" + +if [[ ${INSTALL_SQUID_PROXY} = Y ]]; then + deploy_squid_proxy +fi + +if [[ ${INSTALL_NGINX_LOAD_BALANCER} = Y ]]; then + deploy_nginx_load_balancer +fi diff --git a/scripts/cnode-helper-scripts/mithril-signer.sh b/scripts/cnode-helper-scripts/mithril-signer.sh index 9670460c3..4812e4a5e 100755 --- a/scripts/cnode-helper-scripts/mithril-signer.sh +++ b/scripts/cnode-helper-scripts/mithril-signer.sh @@ -2,7 +2,7 @@ # shellcheck disable=SC2086 #shellcheck source=/dev/null -. "$(dirname $0)"/env offline +. "$(dirname $0)"/mithril.library ###################################### # User Variables - Change as desired # @@ -16,9 +16,6 @@ # Do NOT modify code below # ###################################### -U_ID=$(id -u) -G_ID=$(id -g) - ##################### # Functions # ##################### @@ -26,44 +23,25 @@ G_ID=$(id -g) usage() { cat <<-EOF - Usage: $(basename "$0") [-d] [-u] + Usage: $(basename "$0") [-d] [-D] [-e] [-k] [-r] [-s] [-u] [-h] + A script to setup, run and verify Cardano Mithril Signer - Cardano Mithril signer wrapper script !! -d Deploy mithril-signer as a systemd service - -u Update mithril environment file + -D Run mithril-signer as a daemon + -e Update mithril environment file + -k Stop signer using SIGINT + -r Verify signer registration + -s Verify signer signature + -u Skip update check -h Show this help text EOF } -set_defaults() { - [[ -z "${MITHRILBIN}" ]] && MITHRILBIN="${HOME}"/.local/bin/mithril-signer - if [[ -z "${POOL_NAME}" ]] || [[ "${POOL_NAME}" == "CHANGE_ME" ]]; then - echo "ERROR: The POOL_NAME must be set before deploying mithril-signer as a systemd service!!" - exit 1 - else - case "${NETWORK_NAME,,}" in - mainnet|preprod|guild) - RELEASE="release" - ;; - preview) - RELEASE="pre-release" - ;; - *) - echo "ERROR: The NETWORK_NAME must be set to Mainnet, PreProd, Preview, or Guild before mithril-signer can be deployed!!" - exit 1 - esac - fi -} - -pre_startup_sanity() { - [[ ! -f "${MITHRILBIN}" ]] && MITHRILBIN="$(command -v mithril-signer)" - if [[ ! -S "${CARDANO_NODE_SOCKET_PATH}" ]]; then - echo "ERROR: Could not locate socket file at ${CARDANO_NODE_SOCKET_PATH}, the node may not have completed startup !!" - exit 1 - fi +mithril_init() { # Move logs to archive - [[ -f "${LOG_DIR}"/mithril-signer.log ]] && mv "${LOG_DIR}"/mithril-signer.log "${LOG_DIR}"/archive/ + [[ -d "${LOG_DIR}"/archive ]] || mkdir -p "${LOG_DIR}"/archive + [[ -f "${LOG_DIR}"/$(basename "${0::-3}").log ]] && mv "${LOG_DIR}/$(basename "${0::-3}")".log "${LOG_DIR}"/archive/ } get_relay_endpoint() { @@ -73,55 +51,15 @@ get_relay_endpoint() { echo "Using RELAY_ENDPOINT=${RELAY_ENDPOINT_IP}:${RELAY_PORT} for the Mithril signer relay endpoint." } -generate_environment_file() { - if [[ ! -d "${CNODE_HOME}/mithril/data-stores" ]]; then - sudo mkdir -p "${CNODE_HOME}"/mithril/data-stores - sudo chown -R "$U_ID":"$G_ID" "${CNODE_HOME}"/mithril 2>/dev/null - fi - # Inquire about the relay endpoint - read -r -p "Are you using a relay endpoint? (y/n, press Enter to use default y): " ENABLE_RELAY_ENDPOINT - ENABLE_RELAY_ENDPOINT=${ENABLE_RELAY_ENDPOINT:-y} - if [[ "${ENABLE_RELAY_ENDPOINT}" == "y" ]]; then - get_relay_endpoint - else - echo "Using a naive Mithril configuration without a mithril relay." - fi - - # Generate the full set of environment variables required by Mithril signer use case - export ERA_READER_ADDRESS=https://raw.githubusercontent.com/input-output-hk/mithril/main/mithril-infra/configuration/${RELEASE}-${NETWORK_NAME,,}/era.addr - export ERA_READER_VKEY=https://raw.githubusercontent.com/input-output-hk/mithril/main/mithril-infra/configuration/${RELEASE}-${NETWORK_NAME,,}/era.vkey - sudo bash -c "cat <<-'EOF' > ${CNODE_HOME}/mithril/mithril.env - KES_SECRET_KEY_PATH=${POOL_DIR}/${POOL_HOTKEY_SK_FILENAME} - OPERATIONAL_CERTIFICATE_PATH=${POOL_DIR}/${POOL_OPCERT_FILENAME} - NETWORK=${NETWORK_NAME,,} - RELEASE=${RELEASE} - AGGREGATOR_ENDPOINT=https://aggregator.${RELEASE}-${NETWORK_NAME,,}.api.mithril.network/aggregator - RUN_INTERVAL=60000 - DB_DIRECTORY=${CNODE_HOME}/db - CARDANO_NODE_SOCKET_PATH=${CARDANO_NODE_SOCKET_PATH} - CARDANO_CLI_PATH=${HOME}/.local/bin/cardano-cli - DATA_STORES_DIRECTORY=${CNODE_HOME}/mithril/data-stores - STORE_RETENTION_LIMITS=5 - ERA_READER_ADAPTER_TYPE=cardano-chain - ERA_READER_ADAPTER_PARAMS=$(jq -nc --arg address "$(wget -q -O - "${ERA_READER_ADDRESS}")" --arg verification_key "$(wget -q -O - "${ERA_READER_VKEY}")" '{"address": $address, "verification_key": $verification_key}') - GENESIS_VERIFICATION_KEY=$(wget -q -O - https://raw.githubusercontent.com/input-output-hk/mithril/main/mithril-infra/configuration/${RELEASE}-${NETWORK_NAME,,}/genesis.vkey) - PARTY_ID=$(cat ${POOL_DIR}/${POOL_ID_FILENAME}) - SNAPSHOT_DIGEST=latest - EOF" && sudo chown $USER:$USER "${CNODE_HOME}"/mithril/mithril.env - - if [[ "${ENABLE_RELAY_ENDPOINT}" == "y" ]]; then - sudo bash -c "echo RELAY_ENDPOINT=http://${RELAY_ENDPOINT_IP}:${RELAY_PORT} >> ${CNODE_HOME}/mithril/mithril.env" - fi -} deploy_systemd() { - echo "Creating ${CNODE_VNAME}-mithril-signer systemd service environment file.." + echo "Creating ${CNODE_VNAME}-$(basename "${0::-3}") systemd service environment file.." if [[ ! -f "${CNODE_HOME}"/mithril/mithril.env ]]; then generate_environment_file && echo "Mithril environment file created successfully!!" fi - echo "Deploying ${CNODE_VNAME}-mithril-signer as systemd service.." - sudo bash -c "cat <<-'EOF' > /etc/systemd/system/${CNODE_VNAME}-mithril-signer.service + echo "Deploying ${CNODE_VNAME}-$(basename "${0::-3}") as systemd service.." + sudo bash -c "cat <<-'EOF' > /etc/systemd/system/${CNODE_VNAME}-$(basename "${0::-3}").service [Unit] Description=Cardano Mithril signer service StartLimitIntervalSec=0 @@ -136,29 +74,104 @@ deploy_systemd() { RestartSec=60 User=${USER} EnvironmentFile=${CNODE_HOME}/mithril/mithril.env - ExecStart=/bin/bash -l -c \"exec ${HOME}/.local/bin/mithril-signer -vv\" + ExecStart=/bin/bash -l -c \"exec ${HOME}/.local/bin/$(basename "${0::-3}") -vv\" KillSignal=SIGINT SuccessExitStatus=143 StandardOutput=syslog StandardError=syslog - SyslogIdentifier=${CNODE_VNAME}-mithril-signer + SyslogIdentifier=${CNODE_VNAME}-$(basename "${0::-3}") TimeoutStopSec=5 KillMode=mixed [Install] WantedBy=multi-user.target - EOF" && echo "${CNODE_VNAME}-mithril-signer.service deployed successfully!!" && sudo systemctl daemon-reload && sudo systemctl enable ${CNODE_VNAME}-mithril-signer.service + EOF" && echo "${CNODE_VNAME}-$(basename "${0::-3}").service deployed successfully!!" && sudo systemctl daemon-reload && sudo systemctl enable ${CNODE_VNAME}-"$(basename "${0::-3}")".service +} + +stop_signer() { + CNODE_PID=$(pgrep -fn "$(basename ${CNODEBIN}).*.--port ${CNODE_PORT}" 2>/dev/null) # env was only called in offline mode + kill -2 ${CNODE_PID} 2>/dev/null + # touch clean "${CNODE_HOME}"/db/clean # Disabled as it's a bit hacky, but only runs when SIGINT is passed to node process. Should not be needed if node does it's job + printf " Sending SIGINT to %s process.." "$(basename "${0::-3}")" + sleep 5 + exit 0 +} + + +verify_signer_registration() { + set -e + + if [ -z "$AGGREGATOR_ENDPOINT" ] || [ -z "$PARTY_ID" ]; then + echo ">> ERROR: Required environment variables AGGREGATOR_ENDPOINT and/or PARTY_ID are not set." + exit 1 + fi + + CURRENT_EPOCH=$(curl -s "$AGGREGATOR_ENDPOINT/epoch-settings" -H 'accept: application/json' | jq -r '.epoch') + SIGNERS_REGISTERED_RESPONSE=$(curl -s "$AGGREGATOR_ENDPOINT/signers/registered/$CURRENT_EPOCH" -H 'accept: application/json') + + if echo "$SIGNERS_REGISTERED_RESPONSE" | grep -q "$PARTY_ID"; then + echo ">> Congrats, your signer node is registered!" + else + echo ">> Oops, your signer node is not registered. Party ID not found among the signers registered at epoch ${CURRENT_EPOCH}." + fi + } -################### -# Execution # -################### +verify_signer_signature() { + set -e + + if [ -z "$AGGREGATOR_ENDPOINT" ] || [ -z "$PARTY_ID" ]; then + echo ">> ERROR: Required environment variables AGGREGATOR_ENDPOINT and/or PARTY_ID are not set." + exit 1 + fi + + CERTIFICATES_RESPONSE=$(curl -s "$AGGREGATOR_ENDPOINT/certificates" -H 'accept: application/json') + CERTIFICATES_COUNT=$(echo "$CERTIFICATES_RESPONSE" | jq '. | length') + + echo "$CERTIFICATES_RESPONSE" | jq -r '.[] | .hash' | while read -r HASH; do + RESPONSE=$(curl -s "$AGGREGATOR_ENDPOINT/certificate/$HASH" -H 'accept: application/json') + SIGNER_COUNT=$(echo "$RESPONSE" | jq '.metadata.signers | length') + for (( i=0; i < SIGNER_COUNT; i++ )); do + PARTY_ID_RESPONSE=$(echo "$RESPONSE" | jq -r ".metadata.signers[$i].party_id") + if [[ "$PARTY_ID_RESPONSE" == "$PARTY_ID" ]]; then + echo ">> Congrats, you have signed this certificate: $AGGREGATOR_ENDPOINT/certificate/$HASH !" + exit 1 + fi + done + done + + echo ">> Oops, your party id was not found in the last ${CERTIFICATES_COUNT} certificates. Please try again later." + +} + + +##################### +# Execution / Main # +##################### # Parse command line options -while getopts :duh opt; do +while getopts :dDekrsuh opt; do case ${opt} in - d ) DEPLOY_SYSTEMD="Y" ;; - u ) UPDATE_ENVIRONMENT="Y" ;; + d ) + DEPLOY_SYSTEMD="Y" ;; + D ) + SIGNER_DAEMON="Y" + ;; + e ) + export UPDATE_ENVIRONMENT="Y" + ;; + k ) + STOP_SIGNER="Y" + ;; + r ) + VERIFY_REGISTRATION="Y" + ;; + s ) + VERIFY_SIGNATURE="Y" + ;; + u ) + export SKIP_UPDATE="Y" + ;; h) usage exit 0 @@ -176,32 +189,45 @@ while getopts :duh opt; do esac done -# Check if env file is missing in current folder (no update checks as will mostly run as daemon), source env if present -[[ ! -f "$(dirname $0)"/env ]] && echo -e "\nCommon env file missing, please ensure latest guild-deploy.sh was run and this script is being run from ${CNODE_HOME}/scripts folder! \n" && exit 1 -. "$(dirname $0)"/env -case $? in - 1) echo -e "ERROR: Failed to load common env file\nPlease verify set values in 'User Variables' section in env file or log an issue on GitHub" && exit 1;; - 2) clear ;; -esac +[[ "${STOP_SIGNER}" == "Y" ]] && stop_signer + +# Check for updates +update_check "$@" # Set defaults and do basic sanity checks set_defaults + + #Deploy systemd if -d argument was specified -if [[ "${UPDATE_ENVIRONMENT}" == "Y" && "${DEPLOY_SYSTEMD}" == "Y" ]]; then - generate_environment_file && echo "Environment file updated successfully" && deploy_systemd && echo "Mithril signer service successfully deployed" && exit 0 - exit 2 -elif [[ "${UPDATE_ENVIRONMENT}" == "Y" ]]; then - generate_environment_file && echo "Environment file updated successfully" && exit 0 - exit 2 +if [[ "${UPDATE_ENVIRONMENT}" == "Y" ]]; then + generate_environment_file + exit 0 elif [[ "${DEPLOY_SYSTEMD}" == "Y" ]]; then - deploy_systemd && echo "Mithril signer service successfully deployed" && exit 0 - exit 2 + if deploy_systemd ; then + echo "Mithril signer Systemd service successfully deployed" + exit 0 + else + echo "Failed to deploy Mithril signer Systemd service" + exit 2 + fi fi -pre_startup_sanity - -# Run Mithril Signer Server -echo "Sourcing the Mithril Signer environment file.." +# Source common env variables . "${CNODE_HOME}"/mithril/mithril.env -echo "Starting Mithril Signer Server.." -"${MITHRILBIN}" -vvv >> "${LOG_DIR}"/mithril-signer.log 2>&1 + +if [[ "${VERIFY_REGISTRATION}" == "Y" ]]; then + # Verify signer registration + echo "Verifying Mithril Signer registration.." + verify_signer_registration + exit 0 +elif [[ "${VERIFY_SIGNATURE}" == "Y" ]]; then + # Verify signer signature + echo "Verifying Mithril Signer signature.." + verify_signer_signature + exit 0 +elif [[ "${SIGNER_DAEMON}" == "Y" ]]; then + # Run Mithril Signer Server + echo "Starting Mithril Signer Server.." + "${MITHRILBIN}" -vvv >> "${LOG_DIR}/$(basename "${0::-3}")".log 2>&1 +fi + diff --git a/scripts/cnode-helper-scripts/mithril.library b/scripts/cnode-helper-scripts/mithril.library new file mode 100644 index 000000000..1513d2952 --- /dev/null +++ b/scripts/cnode-helper-scripts/mithril.library @@ -0,0 +1,179 @@ +#!/usr/bin/env bash +# shellcheck disable=SC2034,SC2086,SC2230,SC2206,SC2140,SC2059,SC2154 +#shellcheck source=/dev/null + +. "$(dirname $0)"/env offline + +U_ID=$(id -u) +G_ID=$(id -g) + +compare_versions() { + local min_version=$1 + local test_version=$2 + if [[ $(printf '%s\n' "$min_version" "$test_version" | sort -V | head -n1) = "$min_version" ]]; then + return 0 + else + return 1 + fi +} + +set_node_minimum_version() { + response_file=$(mktemp) + status_code=$(curl -s -o "$response_file" -w "%{http_code}" https://raw.githubusercontent.com/input-output-hk/mithril/${MITHRIL_LATEST_VERSION}/networks.json) + + if [[ "$status_code" -eq 404 ]]; then + NODE_MINIMUM_VERSION="" + else + NODE_MINIMUM_VERSION=$(jq -r ".${NETWORK}.\"cardano-minimum-version\".\"mithril-signer\"" "$response_file") + fi + rm -f "$response_file" +} + +update_check() { + # Check availability of checkUpdate function + if [[ ! $(command -v checkUpdate) ]]; then + echo -e "\nCould not find checkUpdate function in env, make sure you're using official guild docos for installation!" + exit 1 + fi + # Check if flag is set by script to skip update check + [[ ${SKIP_UPDATE} == Y ]] && return 0 + # Check if flag is set by user as a global (container environments etc.) to skip update check + if [[ ${UPDATE_CHECK} = Y ]]; then + echo "Checking for script updates..." + # check for env update + ENV_UPDATED=${BATCH_AUTO_UPDATE} + checkUpdate "${PARENT}"/env N N N + case $? in + 1) ENV_UPDATED=Y ;; + 2) exit 1 ;; + esac + + # check for mithril-relay.sh (self) update + checkUpdate "${PARENT}"/"$(basename "$0")" ${ENV_UPDATED} + case $? in + 1) echo ""; $0 "-u" "$@"; exit 0 ;; # re-launch script with same args skipping update check + 2) exit 1 ;; + esac + fi +} + + +set_defaults() { + MITHRIL_LATEST_VERSION=$(curl -s https://raw.githubusercontent.com/cardano-community/guild-operators/alpha/files/docker/node/release-versions/mithril-latest.txt) + set_node_minimum_version + NODE_CURRENT_VERSION=$(cardano-node --version | awk 'NR==1{print $2}') + + [[ -z "${MITHRILBIN}" ]] && MITHRILBIN="${HOME}"/.local/bin/"$(basename "${0::-3}")" + if [[ $(basename "${0::-3}") == "mithril-signer" ]] && { [[ -z "${POOL_NAME}" ]] || [[ "${POOL_NAME}" == "CHANGE_ME" ]]; }; then + echo "ERROR: The POOL_NAME must be set before deploying mithril-signer as a systemd service!!" + exit 1 + else + case "${NETWORK_NAME,,}" in + mainnet|preprod|guild) + RELEASE="release" + ;; + preview|sanchonet) + RELEASE="pre-release" + ;; + *) + echo "ERROR: The NETWORK_NAME must be set to mainnet, preprod, preview, or sanchonet before $(basename "${0::-3}") can be deployed!!" + exit 1 + esac + fi + AGGREGATOR_ENDPOINT=https://aggregator.${RELEASE}-${NETWORK_NAME,,}.api.mithril.network/aggregator + GENESIS_VERIFICATION_KEY=$(wget -q -O - https://raw.githubusercontent.com/input-output-hk/mithril/main/mithril-infra/configuration/${RELEASE}-${NETWORK_NAME,,}/genesis.vkey) + mithril_init $0 +} + +create_data_stores_directory() { + if [[ ! -d "${CNODE_HOME}/mithril/data-stores" ]]; then + sudo mkdir -p "${CNODE_HOME}"/mithril/data-stores + sudo chown -R "$U_ID":"$G_ID" "${CNODE_HOME}"/mithril 2>/dev/null + fi +} + +set_env_file_ownership() { + chown $USER:$USER "${CNODE_HOME}"/mithril/mithril.env +} + + +check_mithril_environment_file_exists() { + local env_file="${CNODE_HOME}/mithril/mithril.env" + + if [[ -f "$env_file" ]]; then + if [[ "$UPDATE_ENVIRONMENT" != "Y" ]]; then + echo "Error: $env_file already exists. To update it, set UPDATE_ENVIRONMENT to 'Y'." >&2 + return 1 + else + echo "Updating $env_file..." + fi + else + echo "Creating $env_file..." + fi +} + +update_mithril_environment_for_signer() { + echo "Info: POOL_NAME is set. Setting environment variables for Mithril client & signer use cases." + # Inquire about the relay endpoint + read -r -p "Are you using a relay endpoint? (y/n, press Enter to use default y): " ENABLE_RELAY_ENDPOINT + ENABLE_RELAY_ENDPOINT=${ENABLE_RELAY_ENDPOINT:-y} + if [[ "${ENABLE_RELAY_ENDPOINT}" == "y" ]]; then + get_relay_endpoint + else + echo "Using a naive Mithril configuration without a mithril relay." + fi + # Generate the full set of environment variables required by Mithril signer use case + export ERA_READER_ADDRESS=https://raw.githubusercontent.com/input-output-hk/mithril/main/mithril-infra/configuration/${RELEASE}-${NETWORK_NAME,,}/era.addr + export ERA_READER_VKEY=https://raw.githubusercontent.com/input-output-hk/mithril/main/mithril-infra/configuration/${RELEASE}-${NETWORK_NAME,,}/era.vkey + sudo bash -c "cat <<-'EOF' > ${CNODE_HOME}/mithril/mithril.env + KES_SECRET_KEY_PATH=${POOL_DIR}/${POOL_HOTKEY_SK_FILENAME} + OPERATIONAL_CERTIFICATE_PATH=${POOL_DIR}/${POOL_OPCERT_FILENAME} + NETWORK=${NETWORK_NAME,,} + RELEASE=${RELEASE} + AGGREGATOR_ENDPOINT=https://aggregator.${RELEASE}-${NETWORK_NAME,,}.api.mithril.network/aggregator + RUN_INTERVAL=60000 + DB_DIRECTORY=${CNODE_HOME}/db + CARDANO_NODE_SOCKET_PATH=${CARDANO_NODE_SOCKET_PATH} + CARDANO_CLI_PATH=${HOME}/.local/bin/cardano-cli + DATA_STORES_DIRECTORY=${CNODE_HOME}/mithril/data-stores + STORE_RETENTION_LIMITS=5 + ERA_READER_ADAPTER_TYPE=cardano-chain + ERA_READER_ADAPTER_PARAMS=$(jq -nc --arg address "$(wget -q -O - "${ERA_READER_ADDRESS}")" --arg verification_key "$(wget -q -O - "${ERA_READER_VKEY}")" '{"address": $address, "verification_key": $verification_key}') + GENESIS_VERIFICATION_KEY=$(wget -q -O - https://raw.githubusercontent.com/input-output-hk/mithril/main/mithril-infra/configuration/${RELEASE}-${NETWORK_NAME,,}/genesis.vkey) + PARTY_ID=$(cat ${POOL_DIR}/${POOL_ID_FILENAME}) + SNAPSHOT_DIGEST=latest + EOF" + + if [[ "${ENABLE_RELAY_ENDPOINT}" == "y" ]]; then + sudo bash -c "echo RELAY_ENDPOINT=http://${RELAY_ENDPOINT_IP}:${RELAY_PORT} >> ${CNODE_HOME}/mithril/mithril.env" + fi +} + +update_mithril_environment_for_client() { + echo "Info: POOL_NAME is not set. Setting minimal environment variables for a Mithril client use case." + bash -c "cat <<-'EOF' > ${CNODE_HOME}/mithril/mithril.env + NETWORK=${NETWORK_NAME,,} + RELEASE=${RELEASE} + AGGREGATOR_ENDPOINT=https://aggregator.${RELEASE}-${NETWORK_NAME,,}.api.mithril.network/aggregator + DB_DIRECTORY=${CNODE_HOME}/db + GENESIS_VERIFICATION_KEY=$(wget -q -O - https://raw.githubusercontent.com/input-output-hk/mithril/main/mithril-infra/configuration/${RELEASE}-${NETWORK_NAME,,}/genesis.vkey) + SNAPSHOT_DIGEST=latest + EOF" +} + +component_environment_setup() { + check_mithril_environment_file_exists + + if [[ -n "${POOL_NAME}" ]] && [[ "${POOL_NAME}" != "CHANGE_ME" ]]; then + update_mithril_environment_for_signer + else + update_mithril_environment_for_client + fi +} + +generate_environment_file() { + create_data_stores_directory + component_environment_setup + set_env_file_ownership +} +