diff --git a/default.env b/default.env index 41245f49..a1fbbea5 100644 --- a/default.env +++ b/default.env @@ -8,7 +8,11 @@ FEE_RECIPIENT= # If "true" and used with a CL, it also requires :mev-boost.yml in COMPOSE_FILE MEV_BOOST=false # For relay information, please see https://ethstaker.cc/mev-relay-list/ -MEV_RELAYS=https://0xafa4c6985aa049fb79dd37010438cfebeb0f2bd42b115b89dd678dab0670c1de38da0c4e9138c9290a398ecd9a0b3110@boost-relay-holesky.flashbots.net,https://0xaa58208899c6105603b74396734a6263cc7d947f444f396a90f7b7d3e65d102aec7e5e5291b27e08d02c50a050825c2f@holesky.titanrelay.xyz,https://0x821f2a65afb70e7f2e820a925a9b4c80a159620582c1766b1b09729fec178b11ea22abb3a51f07b288be815a1a2ff516@bloxroute.holesky.blxrbdn.com +MEV_RELAYS=" +https://0xafa4c6985aa049fb79dd37010438cfebeb0f2bd42b115b89dd678dab0670c1de38da0c4e9138c9290a398ecd9a0b3110@boost-relay-holesky.flashbots.net, +https://0xaa58208899c6105603b74396734a6263cc7d947f444f396a90f7b7d3e65d102aec7e5e5291b27e08d02c50a050825c2f@holesky.titanrelay.xyz, +https://0x821f2a65afb70e7f2e820a925a9b4c80a159620582c1766b1b09729fec178b11ea22abb3a51f07b288be815a1a2ff516@bloxroute.holesky.blxrbdn.com +" # Set a minimum MEV bid (e.g. 0.05), used by mev-boost.yml. If empty, no minimum is used. MEV_MIN_BID= # Graffiti to use for validator diff --git a/ethd b/ethd index 943f9274..098577d5 100755 --- a/ethd +++ b/ethd @@ -947,7 +947,7 @@ __upgrade_postgres() { # This gets used, but shellcheck doesn't recognize that # shellcheck disable=SC2034 PG_DOCKER_TAG=${__target_pg}-bookworm # To bookworm to avoid collation errors - also a faster PostgreSQL - __set_value_in_env + __update_value_in_env "${__var}" "${!__var}" "${__env_file}" echo "Web3signer has been stopped. You'll need to run \"$__me up\" to start it again." echo echo "A copy of your old slashing protection database is in the Docker volume ${__backup_vol}." @@ -1021,12 +1021,140 @@ __enable_v6() { if [ "${__v6_works}" = "true" ]; then echo "Enabling IPv4/6 dual-stack for your Eth Docker setup" IPV6="true" - __set_value_in_env + __update_value_in_env "${__var}" "${!__var}" "${__env_file}" __enabled_v6=1 fi } +__get_value_from_env() { + local __var_name="$1" + local __env_file="$2" + local __value + + __value=$(awk -v var="$__var_name" ' + BEGIN { __found = 0; __value = "" } + + # Skip empty lines and comments + /^#|^\s*$/ { + next + } + + # Match single-line unquoted value + $0 ~ "^[ \t]*"var"=[^\"].*$" { + gsub("^[ \t]*"var"=", "") + gsub(/^[ \t]*|[ \t]*$/, "", $0) + __value = $0 + __found = 1 + exit + } + + # Match a quoted single-line value + $0 ~ "^[ \t]*"var"=\"[^\"]*\"[ \t]*$" { + gsub("^[ \t]*"var"=\"", "") + gsub(/\"[ \t]*$/, "", $0) + __value = "\"" $0 "\"" + __found = 1 + exit + } + + # Match the start of a multi-line value (with opening quote) + $0 ~ "^[ \t]*"var"=\"[^\"]*$" { + gsub("^[ \t]*"var"=\"", "") + __value = "\"" $0 "\n" + __found = 1 + next + } + + # Continue collecting lines for a multi-line value + __found && !/\"[ \t]*$/ { + __value = __value $0 "\n" + next + } + + # End of a multi-line value (with closing quote) + __found && /\"[ \t]*$/ { + gsub(/[ \t]*\"[ \t]*$/, "") + __value = __value $0 "\"" + __found = 1 + exit + } + + END { + if (__found) { + # Print the value as is, including quotes for multi-line + print __value + } + } + ' "$__env_file") + + printf "%s" "$__value" +} + + +__update_value_in_env() { +# Call as __update_value_in_env "$__var" "$__value" "$__env_file" + local __var_name="$1" + local __new_value="$2" + local __env_file="$3" + + # Escape backslashes for safety + local __escaped_value + __escaped_value=$(printf '%s' "${__new_value}" | sed 's/\\/\\\\/g') + + # Check if the variable already exists in the .env file + if grep -q "^[ \t]*${__var_name}=" "${__env_file}"; then + # Variable exists, update it + awk -v var="$__var_name" -v new_value="$__escaped_value" ' + BEGIN { in_block = 0; multi_line = 0 } + + # Match the line that starts with the variable name + $0 ~ "^[ \t]*" var "=" { + # If the value starts with a quote, it is a multi-line + if ($0 ~ "^[ \t]*" var "=\"") { + # Start of multi-line value + multi_line = 1 + # Print the variable name with the new value, replacing & safely + gsub(/&/, "\\&", new_value) + print var "=" new_value + } else { + # Single-line value + gsub(/&/, "\\&", new_value) + print var "=" new_value + } + # Set the flag to indicate we are processing the target variable block + in_block = 1 + next + } + + # If we encounter a new variable definition, stop skipping lines + /^[A-Z_][A-Z0-9_]*=/ && in_block { + in_block = 0 + multi_line = 0 + } + + # Continue to skip lines in a multi-line block if multi_line is true + multi_line && !/\"[ \t]*$/ { + next + } + + # If we reach the end of a multi-line value, reset flags + multi_line && /\"[ \t]*$/ { + in_block = 0 + multi_line = 0 + next + } + + # Print all lines if not in the target variable block + { print } + ' "$__env_file" > "${__env_file}.tmp" && mv "${__env_file}.tmp" "$__env_file" + else + # Variable does not exist, append it + printf "%s=%s\n" "$__var_name" "$__escaped_value" >> "$__env_file" + fi +} + + __env_migrate() { if [ ! -f "${__env_file}" ]; then return 0 @@ -1066,7 +1194,7 @@ __env_migrate() { SIREN_PASSWORD=$(sed -n -e "s/^${__var}=\(.*\)/\1/p" "${__env_file}" || true) if [ -z "${SIREN_PASSWORD}" ]; then SIREN_PASSWORD=$(head -c 8 /dev/urandom | od -A n -t u8 | tr -d '[:space:]' | sha256sum | head -c 32) - __set_value_in_env + __update_value_in_env "${__var}" "${!__var}" "${__env_file}" fi __var=ENV_VERSION @@ -1093,7 +1221,7 @@ __env_migrate() { ${__as_owner} cp default.env "${__env_file}" __var="COMPOSE_FILE" - __value=$(sed -n -e "s/^${__var}=\(.*\)/\1/p" "${__env_file}.source" || true) + __value=$(__get_value_from_env "${__var}" "${__env_file}.source") # Literal match intended # shellcheck disable=SC2076 if [[ "${__value}" =~ "blox-ssv2.yml" ]]; then @@ -1102,7 +1230,8 @@ __env_migrate() { # Migrate over user settings for __var in "${__all_vars[@]}"; do - __value=$(sed -n -e "s/^${__var}=\(.*\)/\1/p" "${__env_file}.source" || true) + __value=$(__get_value_from_env "${__var}" "${__env_file}.source") + # __value=$(sed -n -e "s/^${__var}=\(.*\)/\1/p" "${__env_file}.source" || true) if [ -n "${__value}" ] || [ "${__var}" = "GRAFFITI" ] || [ "${__var}" = "MEV_RELAYS" ] \ || [ "${__var}" = "ETH_DOCKER_TAG" ] || [ "${__var}" = "RAPID_SYNC_URL" ] \ || [ "${__var}" = "OBOL_CHARON_CL_ENDPOINTS" ]; then @@ -1145,13 +1274,13 @@ __env_migrate() { if [[ "${__var}" = "SHARE_IP" && "${__value: -1}" = ":" ]]; then __value="${__value%:}" # Undo Compose V1 accommodation fi - # Handle & in GRAFFITI gracefully - sed -i'.original' -e "s~^\(${__var}\s*=\s*\).*\$~\1${__value//&/\\&}~" "${__env_file}" + # Handle & in GRAFFITI gracefully, as well as multi-line + __update_value_in_env "${__var}" "$__value" "${__env_file}" else # empty __value if [ "${__var}" = "CF_ZONE_ID" ]; then __lookup_cf_zone if [ -n "${__value}" ]; then - sed -i'.original' -e "s~^\(${__var}\s*=\s*\).*\$~\1${__value//&/\\&}~" "${__env_file}" + __update_value_in_env "${__var}" "$__value" "${__env_file}" fi fi fi @@ -1204,7 +1333,7 @@ __env_migrate() { Eth Docker docs (https://ethdocker.com/About/Rewards) for more information.\n\nCAUTION: \"$__me up\" will fail if no \ valid address is set" 12 75 __query_coinbase - __set_value_in_env + __update_value_in_env "${__var}" "${!__var}" "${__env_file}" else echo "A fee recipient ETH wallet address is required in order to start the client. Please set one in \".env\"." echo "CAUTION: \"$__me up\" will fail if no valid address is set." @@ -2139,7 +2268,7 @@ __i_haz_ethdo() { COMPOSE_FILE="ethdo.yml" echo "You do not have a CL in ${__project_name}. Please make sure CL_NODE in ${__env_file} points at an available one" fi - __set_value_in_env + __update_value_in_env "${__var}" "${!__var}" "${__env_file}" echo "Your COMPOSE_FILE now reads ${COMPOSE_FILE}" fi } @@ -2178,7 +2307,7 @@ __i_haz_web3signer() { echo "You do not have a validator client in ${__project_name}. web3signer cannot be used without one." exit 1 fi - __set_value_in_env + __update_value_in_env "${__var}" "${!__var}" "${__env_file}" echo "Your COMPOSE_FILE now reads ${COMPOSE_FILE}" fi } @@ -3057,7 +3186,7 @@ https://0x98650451ba02064f7b000f5768cf0cf4d4e492317d82871bdc87ef841a0743f69f0f1e return 0 fi __var="MEV_BOOST" - __value=$(sed -n -e "s/^${__var}=\(.*\)/\1/p" "${__env_file}" || true) + __value=$(__get_value_from_env "${__var}" "${__env_file}") # I do mean to match literally # shellcheck disable=SC2076 if [[ "${CONSENSUS_CLIENT}" =~ "-vc-only.yml" ]]; then @@ -3157,7 +3286,7 @@ want to use MEV Boost?" 10 65); then MEV_BOOST="true" if [ "${__value}" = "true" ]; then __var="MEV_RELAYS" - MEV_RELAYS=$(sed -n -e "s/^${__var}=\(.*\)/\1/p" "${__env_file}" || true) + MEV_RELAYS=$(__get_value_from_env "${__var}" "${__env_file}") else case "${NETWORK}" in "sepolia") @@ -3184,8 +3313,14 @@ https://0x8c7d33605ecef85403f8b7289c8058f440cbb6bf72b055dfe2f3e2c6695b6a1ea5a9cd ;; esac fi - MEV_RELAYS=$(whiptail --title "Configure MEV relays" --inputbox "What MEV relay(s) do you want to use? \ -(right-click to paste)" 10 65 "${MEV_RELAYS}" 3>&1 1>&2 2>&3) + # Replace newlines with "\n" for the whiptail input + __formatted_mev_relays=$(printf '%s' "${MEV_RELAYS}" | sed ':a;N;$!ba;s/\n/\\n/g') + + __formatted_mev_relays=$(whiptail --title "Configure MEV relays" --inputbox "What MEV relay(s) do you want to use? \ +(right-click to paste)" 15 65 "${__formatted_mev_relays}" 3>&1 1>&2 2>&3) + + # Replace "\n" back to newlines to restore multi-line format + MEV_RELAYS=$(printf '%s' "${__formatted_mev_relays}" | sed 's/\\n/\n/g') echo "Your MEV relay(s): ${MEV_RELAYS}" else MEV_BOOST="false" @@ -3356,18 +3491,6 @@ __query_dkg() { rm -f ssv-config/dkg-config.yaml.original } -__set_value_in_env() { -# Assumes that "__var" has been set to the name of the variable to be changed - if [ "${!__var+x}" ]; then - if ! grep -qF "${__var}" "${__env_file}" 2>/dev/null ; then - echo "${__var}=${!__var}" >> "${__env_file}" - else -# Handle & in GRAFFITI gracefully - sed -i'.original' -e "s~^\(${__var}\s*=\s*\).*\$~\1${!__var//&/\\&}~" "${__env_file}" - fi - fi -} - __handle_error() { if [[ ! $- =~ e ]]; then @@ -3678,7 +3801,8 @@ config() { if [ "${__deployment}" = "lido_csm" ]; then COMPOSE_FILE="${COMPOSE_FILE}:deposit-cli.yml" fi -# Not multi-arch, this would break on ARM64 +# Not multi-arch, this would break on ARM64:w + # COMPOSE_FILE="${COMPOSE_FILE}:ethdo.yml" if [ "${__deployment}" = "rocket" ]; then COMPOSE_FILE="${COMPOSE_FILE}:ext-network.yml" @@ -3688,72 +3812,70 @@ config() { echo "Your COMPOSE_FILE is:" "${COMPOSE_FILE}" __var=FEE_RECIPIENT - __set_value_in_env + __update_value_in_env "${__var}" "${!__var}" "${__env_file}" __var=GRAFFITI - __set_value_in_env + __update_value_in_env "${__var}" "${!__var}" "${__env_file}" __var=CL_NODE - __set_value_in_env + __update_value_in_env "${__var}" "${!__var}" "${__env_file}" __var=RAPID_SYNC_URL - __set_value_in_env + __update_value_in_env "${__var}" "${!__var}" "${__env_file}" __var=COMPOSE_FILE - __set_value_in_env + __update_value_in_env "${__var}" "${!__var}" "${__env_file}" __var=EL_NODE - __set_value_in_env + __update_value_in_env "${__var}" "${!__var}" "${__env_file}" __var=JWT_SECRET - __set_value_in_env + __update_value_in_env "${__var}" "${!__var-}" "${__env_file}" __var=NETWORK - __set_value_in_env + __update_value_in_env "${__var}" "${!__var}" "${__env_file}" __var=MEV_BOOST - __set_value_in_env + __update_value_in_env "${__var}" "${!__var}" "${__env_file}" __var=MEV_RELAYS - __set_value_in_env + __update_value_in_env "${__var}" "${!__var}" "${__env_file}" if [ "${__deployment}" = "lido_obol" ]; then var=LIDO_DV_EXIT_EXIT_EPOCH - __set_value_in_env + __update_value_in_env "${__var}" "${!__var}" "${__env_file}" var=VE_OPERATOR_ID - __set_value_in_env + __update_value_in_env "${__var}" "${!__var}" "${__env_file}" var=VE_LOCATOR_ADDRESS - __set_value_in_env + __update_value_in_env "${__var}" "${!__var}" "${__env_file}" var=VE_ORACLE_ADDRESSES_ALLOWLIST - __set_value_in_env + __update_value_in_env "${__var}" "${!__var}" "${__env_file}" var=VE_STAKING_MODULE_ID - __set_value_in_env + __update_value_in_env "${__var}" "${!__var}" "${__env_file}" # We are using the variable # shellcheck disable=SC2034 ENABLE_DIST_ATTESTATION_AGGR="true" var=ENABLE_DIST_ATTESTATION_AGGR - __set_value_in_env + __update_value_in_env "${__var}" "${!__var}" "${__env_file}" fi if [[ "${NETWORK}" = "gnosis" ]] && [[ "${CONSENSUS_CLIENT}" =~ "nimbus" ]] ; then # We are using the variable # shellcheck disable=SC2034 NIM_DOCKERFILE=Dockerfile.sourcegnosis __var=NIM_DOCKERFILE - __set_value_in_env + __update_value_in_env "${__var}" "${!__var}" "${__env_file}" fi if uname -m | grep -q riscv64; then # We are using the variable # shellcheck disable=SC2034 NIM_DOCKERFILE=Dockerfile.source __var=NIM_DOCKERFILE - __set_value_in_env + __update_value_in_env "${__var}" "${!__var}" "${__env_file}" # We are using the variable # shellcheck disable=SC2034 GETH_DOCKERFILE=Dockerfile.source __var=GETH_DOCKERFILE - __set_value_in_env + __update_value_in_env "${__var}" "${!__var}" "${__env_file}" fi __var="SIREN_PASSWORD" SIREN_PASSWORD=$(sed -n -e "s/^${__var}=\(.*\)/\1/p" "${__env_file}" || true) if [ -z "${SIREN_PASSWORD}" ]; then SIREN_PASSWORD=$(head -c 8 /dev/urandom | od -A n -t u8 | tr -d '[:space:]' | sha256sum | head -c 32) - __set_value_in_env + __update_value_in_env "${__var}" "${!__var}" "${__env_file}" fi __enable_v6 - ${__as_owner} rm .env.original - __pull_and_build __nag_os_version