#!/usr/bin/env bash

# Cardano local cluster startup script
# This script sets up and starts a local Cardano test cluster, bootstrapping it through
# all eras from Byron to Conway, registering pools and governance entities.

set -Eeuo pipefail
trap 'echo "Error at line $LINENO in ${BASH_SOURCE[0]}" >&2' ERR

readonly PROTOCOL_VERSION="${PROTOCOL_VERSION:-10}"
readonly NUM_BFT_NODES=1
readonly NUM_CC=5
readonly NUM_DREPS=5
readonly TX_SUBMISSION_DELAY=60
readonly PROPOSAL_DELAY=5
readonly SUBMIT_DELAY=5
readonly POOL_PLEDGE=1000000000000
readonly DREP_DELEGATED=500000000000
readonly FEE=5000000

readonly INSTANCE_NUM="%%INSTANCE_NUM%%"
readonly NUM_POOLS=%%NUM_POOLS%%
readonly NODE_PORT_BASE=%%NODE_PORT_BASE%%
readonly PORTS_PER_NODE=%%PORTS_PER_NODE%%
readonly WEBSERVER_PORT=%%WEBSERVER_PORT%%

initialize_globals() {
  if [ -z "${CARDANO_NODE_SOCKET_PATH:-}" ]; then
    echo "CARDANO_NODE_SOCKET_PATH is not set" >&2
    exit 1
  fi

  SCRIPT_DIR="$(readlink -m "${0%/*}")"
  readonly SCRIPT_DIR
  SOCKET_PATH="$(readlink -m "$CARDANO_NODE_SOCKET_PATH")"
  readonly SOCKET_PATH
  readonly STATE_CLUSTER="${SOCKET_PATH%/*}"

  if [ "$PWD" = "$STATE_CLUSTER" ]; then
    echo "Please run this script from outside of '$STATE_CLUSTER'" >&2
    exit 1
  fi

  readonly STATE_CLUSTER_NAME="${STATE_CLUSTER##*/}"
  readonly SUPERVISORD_SOCKET_PATH="${STATE_CLUSTER}/supervisord.sock"
  readonly START_CLUSTER_STATUS="${STATE_CLUSTER}/status_started"
  readonly PPARAMS_FILE="${STATE_CLUSTER}/pparams.json"

  if [[ "$SOCKET_PATH" != *"/state-cluster${INSTANCE_NUM}/"* ]]; then
    echo "CARDANO_NODE_SOCKET_PATH must be set to a path containing 'state-cluster${INSTANCE_NUM}', line $LINENO in ${BASH_SOURCE[0]}" >&2
    exit 1
  fi

  if [ -e "$SUPERVISORD_SOCKET_PATH" ]; then
    echo "Cluster already running. Please run \`${STATE_CLUSTER}/stop-cluster\` first!" >&2
    exit 1
  fi

  if [ "$NUM_POOLS" -lt 3 ]; then
    echo "NUM_POOLS must be at least 3" >&2
    exit 1
  fi

  case "${UTXO_BACKEND:=""}" in
    "" | mem | disk | disklmdb | empty)
      ;;
    *)
      echo "Unknown \`UTXO_BACKEND\`: '$UTXO_BACKEND', line $LINENO in ${BASH_SOURCE[0]}" >&2
      exit 1
      ;;
  esac
  readonly UTXO_BACKEND

  local backend
  UTXO_BACKENDS=()
  for backend in ${MIXED_UTXO_BACKENDS:=""}; do
    case "$backend" in
      mem | disk | disklmdb | empty)
        UTXO_BACKENDS+=("$backend")
        ;;
      *)
        echo "Unknown \`MIXED_UTXO_BACKENDS\` entry: '$backend', line $LINENO in ${BASH_SOURCE[0]}" >&2
        exit 1
        ;;
    esac
  done
  readonly UTXO_BACKENDS

  SECURITY_PARAM="$(jq '.securityParam' < "${SCRIPT_DIR}/genesis.spec.json")"
  readonly SECURITY_PARAM
  NETWORK_MAGIC="$(jq '.networkMagic' < "${SCRIPT_DIR}/genesis.spec.json")"
  readonly NETWORK_MAGIC
  MAX_SUPPLY="$(jq '.maxLovelaceSupply' < "${SCRIPT_DIR}/genesis.spec.json")"
  readonly MAX_SUPPLY
  POOL_COST="$(jq '.protocolParams.minPoolCost' < "${SCRIPT_DIR}/genesis.spec.json")"
  if [ "$POOL_COST" -eq 0 ]; then
    POOL_COST=600
  fi
  readonly POOL_COST

  # shellcheck disable=SC1091
  source "${SCRIPT_DIR}/common.sh"

  if [ -e "${SCRIPT_DIR}/shell_env" ]; then
    # shellcheck disable=SC1091
    source "${SCRIPT_DIR}/shell_env"
  fi

  readonly FUNDS_PER_GENESIS_ADDRESS="$((MAX_SUPPLY / NUM_BFT_NODES))"
  readonly FUNDS_PER_BYRON_ADDRESS="$((FUNDS_PER_GENESIS_ADDRESS * 8 / 10))"
}

create_genesis() {
  local start_time
  local start_time_shelley

  start_time_shelley="$(date --utc +"%Y-%m-%dT%H:%M:%SZ" --date="5 seconds")"
  start_time="$(date +%s --date="$start_time_shelley")"
  echo "$start_time" > "${STATE_CLUSTER}/cluster_start_time"

  # Create Byron genesis

  cardano_cli_log byron genesis genesis \
    --protocol-magic "$NETWORK_MAGIC" \
    --k "$SECURITY_PARAM" \
    --n-poor-addresses 0 \
    --n-delegate-addresses "$NUM_BFT_NODES" \
    --total-balance "$MAX_SUPPLY" \
    --delegate-share 1 \
    --avvm-entry-count 0 \
    --avvm-entry-balance 0 \
    --protocol-parameters-file "${STATE_CLUSTER}/byron-params.json" \
    --genesis-output-dir "${STATE_CLUSTER}/byron" \
    --start-time "$start_time"

  mv "${STATE_CLUSTER}/byron-params.json" "${STATE_CLUSTER}/byron/params.json"

  # Create Shelley genesis and subsequent era genesis files

  local genesis_args=( \
    --genesis-dir "${STATE_CLUSTER}/shelley" \
    --testnet-magic "$NETWORK_MAGIC" \
    --gen-genesis-keys "$NUM_BFT_NODES" \
    --start-time "$start_time_shelley" \
    --gen-utxo-keys 1
  )

  if ! cardano_cli_log legacy genesis create --shelley "${genesis_args[@]}"; then
    echo "Never mind the error above, retrying the genesis create with legacy arguments" >&2
    cardano_cli_log legacy genesis create "${genesis_args[@]}"
  fi

  if [ ! -e "${STATE_CLUSTER}/shelley/genesis.dijkstra.json" ] \
    && [ -e "${STATE_CLUSTER}/shelley/genesis.dijkstra.spec.json" ]; then
    cp "${STATE_CLUSTER}/shelley/genesis.dijkstra.spec.json" \
      "${STATE_CLUSTER}/shelley/genesis.dijkstra.json"
  fi

  jq -r '
    .initialFunds = {}' \
    < "${STATE_CLUSTER}/shelley/genesis.json" > "${STATE_CLUSTER}/shelley/genesis.json_jq"
  cat "${STATE_CLUSTER}/shelley/genesis.json_jq" > "${STATE_CLUSTER}/shelley/genesis.json"
  rm -f "${STATE_CLUSTER}/shelley/genesis.json_jq"

  jq '.costModels.PlutusV2 |= .[0:175]' \
    < "${STATE_CLUSTER}/shelley/genesis.alonzo.json" > "${STATE_CLUSTER}/shelley/genesis.alonzo.json_jq"
  cat "${STATE_CLUSTER}/shelley/genesis.alonzo.json_jq" > "${STATE_CLUSTER}/shelley/genesis.alonzo.json"
  rm -f "${STATE_CLUSTER}/shelley/genesis.alonzo.json_jq"
}

get_genesis_data() {
  KEY_DEPOSIT="$(jq '.protocolParams.keyDeposit' \
    < "${STATE_CLUSTER}/shelley/genesis.json")"
  readonly KEY_DEPOSIT
  POOL_DEPOSIT="$(jq '.protocolParams.poolDeposit' \
    < "${STATE_CLUSTER}/shelley/genesis.json")"
  readonly POOL_DEPOSIT
  DREP_DEPOSIT="$(jq '.dRepDeposit' \
    < "${STATE_CLUSTER}/shelley/genesis.conway.json")"
  readonly DREP_DEPOSIT
  GOV_ACTION_DEPOSIT="$(jq '.govActionDeposit' \
    < "${STATE_CLUSTER}/shelley/genesis.conway.json")"
  readonly GOV_ACTION_DEPOSIT

  BYRON_GENESIS_HASH="$(cardano_cli_log byron genesis print-genesis-hash --genesis-json \
    "${STATE_CLUSTER}/byron/genesis.json")"
  readonly BYRON_GENESIS_HASH
  SHELLEY_GENESIS_HASH="$(cardano_cli_log latest genesis hash --genesis \
    "${STATE_CLUSTER}/shelley/genesis.json")"
  readonly SHELLEY_GENESIS_HASH
  ALONZO_GENESIS_HASH="$(cardano_cli_log latest genesis hash --genesis \
    "${STATE_CLUSTER}/shelley/genesis.alonzo.json")"
  readonly ALONZO_GENESIS_HASH
  CONWAY_GENESIS_HASH="$(cardano_cli_log latest genesis hash --genesis \
    "${STATE_CLUSTER}/shelley/genesis.conway.json")"
  readonly CONWAY_GENESIS_HASH
  DIJKSTRA_GENESIS_HASH=""
  if [ "$PROTOCOL_VERSION" -ge 11 ] || is_truthy "${ENABLE_EXPERIMENTAL:-}"; then
    DIJKSTRA_GENESIS_HASH="$(cardano_cli_log latest genesis hash --genesis \
      "${STATE_CLUSTER}/shelley/genesis.dijkstra.json")"
  fi
  readonly DIJKSTRA_GENESIS_HASH
}

edit_node_configs() {
  local live_tables_base="${STATE_CLUSTER_NAME}/lmdb"
  local conf fname node_name pool_num utxo_backend

  for conf in "$SCRIPT_DIR"/config-*.json; do
    [ -e "$conf" ] || { echo "No config files found in ${SCRIPT_DIR}, line $LINENO in ${BASH_SOURCE[0]}" >&2; exit 1; }
    fname="${conf##*/}"
    node_name="${fname##config-}"
    node_name="${node_name%.json}"

    if [ "$fname" = "config-bft1.json" ]; then
      pool_num=""
    else
      pool_num="${fname##*-pool}"
      pool_num="${pool_num%.json}"
    fi

    utxo_backend="${UTXO_BACKEND}"
    # Rotate through the mixed backends for block producing nodes, if set.
    if [ -n "$pool_num" ] && [ "${#UTXO_BACKENDS[@]}" -gt 0 ]; then
      index=$(( (pool_num - 1) % ${#UTXO_BACKENDS[@]} ))
      utxo_backend="${UTXO_BACKENDS[$index]}"
    fi
    if [ "$utxo_backend" = "empty" ]; then
      utxo_backend=""
    fi


    jq \
      --arg byron_hash "$BYRON_GENESIS_HASH" \
      --arg shelley_hash "$SHELLEY_GENESIS_HASH" \
      --arg alonzo_hash "$ALONZO_GENESIS_HASH" \
      --arg conway_hash "$CONWAY_GENESIS_HASH" \
      --arg dijkstra_hash "$DIJKSTRA_GENESIS_HASH" \
      --arg backend "$utxo_backend" \
      --arg live_tables_path "${live_tables_base}-${node_name}" \
      '.ByronGenesisHash = $byron_hash
      | .ShelleyGenesisHash = $shelley_hash
      | .AlonzoGenesisHash = $alonzo_hash
      | .ConwayGenesisHash = $conway_hash
      | if $dijkstra_hash != "" then
          (.DijkstraGenesisFile = "shelley/genesis.dijkstra.json"
            | .DijkstraGenesisHash = $dijkstra_hash
            | .ExperimentalProtocolsEnabled = true
            | .ExperimentalHardForksEnabled = true)
        else
          .
        end
      | if $backend == "mem" then
          (.LedgerDB.Backend = "V2InMemory"
           | .LedgerDB.SnapshotInterval = 216)
        elif $backend == "disk" then
          .LedgerDB.Backend = "V2LSM"
        elif $backend == "disklmdb" then
          (.LedgerDB.Backend = "V1LMDB"
           | .LedgerDB.LiveTablesPath = $live_tables_path
           | .LedgerDB.SnapshotInterval = 300)
        elif has("LedgerDB") then
          .LedgerDB |= del(.Backend)
        else
          .
        end
      | if (.LedgerDB? // {}) == {} then del(.LedgerDB) else . end
      ' "$conf" > "${STATE_CLUSTER}/${fname}"

    if ! is_truthy "${ENABLE_LEGACY:-}"; then
      if is_truthy "${MIXED_P2P:-}"; then
        if [ -z "$pool_num" ]; then
          cp -f "$SCRIPT_DIR"/topology-bft1.json "$STATE_CLUSTER"
          continue
        fi

        if [ "$((pool_num % 2))" != 0 ]; then
          cp -f "${SCRIPT_DIR}/topology-pool${pool_num}.json" "$STATE_CLUSTER"
          continue
        fi
      fi

      jq \
        '.EnableP2P = true
        | .MaxConcurrencyBulkSync = 2
        | .MaxConcurrencyDeadline = 4
        | .TargetNumberOfRootPeers = 100
        | .TargetNumberOfKnownPeers = 100
        | .TargetNumberOfEstablishedPeers = 50
        | .TargetNumberOfActivePeers = 20
        | .TraceBlockFetchClient = true
        | .TraceChainSyncClient = true' \
        "${STATE_CLUSTER}/${fname}" > "${STATE_CLUSTER}/${fname}_jq"
      cat "${STATE_CLUSTER}/${fname}_jq" > "${STATE_CLUSTER}/${fname}"
      rm -f "${STATE_CLUSTER}/${fname}_jq"
    fi
  done
}

create_bft_nodes_files() {
  local i index bft_port

  for i in $(seq 1 "$NUM_BFT_NODES"); do
    mkdir -p "${STATE_CLUSTER}/nodes/node-bft$i"
    ln -s "../../shelley/delegate-keys/delegate${i}.vrf.skey" "${STATE_CLUSTER}/nodes/node-bft${i}/vrf.skey"
    ln -s "../../shelley/delegate-keys/delegate${i}.vrf.vkey" "${STATE_CLUSTER}/nodes/node-bft${i}/vrf.vkey"

    cardano_cli_log latest node key-gen-KES \
      --verification-key-file "${STATE_CLUSTER}/nodes/node-bft${i}/kes.vkey" \
      --signing-key-file "${STATE_CLUSTER}/nodes/node-bft${i}/kes.skey"

    cardano_cli_log latest node issue-op-cert \
      --kes-period 0 \
      --cold-signing-key-file "${STATE_CLUSTER}/shelley/delegate-keys/delegate${i}.skey" \
      --kes-verification-key-file "${STATE_CLUSTER}/nodes/node-bft${i}/kes.vkey" \
      --operational-certificate-issue-counter-file \
        "${STATE_CLUSTER}/shelley/delegate-keys/delegate${i}.counter" \
      --out-file "${STATE_CLUSTER}/nodes/node-bft${i}/op.cert"

    index="$(printf "%03d" $((i - 1)))"

    cardano_cli_log byron key keygen \
      --secret "${STATE_CLUSTER}/byron/payment-keys.${index}.key"

    cardano_cli_log byron key signing-key-address \
      --byron-formats \
      --testnet-magic "$NETWORK_MAGIC" \
      --secret "${STATE_CLUSTER}/byron/payment-keys.${index}.key" > "${STATE_CLUSTER}/byron/address-${index}"

    cardano_cli_log byron key signing-key-address \
      --byron-formats  \
      --testnet-magic "$NETWORK_MAGIC" \
      --secret "${STATE_CLUSTER}/byron/genesis-keys.${index}.key" \
        > "${STATE_CLUSTER}/byron/genesis-address-${index}"

    ln -s "../../byron/delegate-keys.${index}.key" "${STATE_CLUSTER}/nodes/node-bft${i}/byron-deleg.key"
    ln -s "../../byron/delegation-cert.${index}.json" "${STATE_CLUSTER}/nodes/node-bft${i}/byron-deleg.json"

    cardano_cli_log byron transaction issue-genesis-utxo-expenditure \
      --genesis-json "${STATE_CLUSTER}/byron/genesis.json" \
      --testnet-magic "$NETWORK_MAGIC" \
      --byron-formats \
      --tx "${STATE_CLUSTER}/byron/tx${i}.tx" \
      --wallet-key "${STATE_CLUSTER}/nodes/node-bft${i}/byron-deleg.key" \
      --rich-addr-from "$(head -n 1 "${STATE_CLUSTER}/byron/genesis-address-${index}")" \
      --txout "(\"$(head -n 1 "${STATE_CLUSTER}/byron/address-${index}")\", ${FUNDS_PER_BYRON_ADDRESS})"

    cardano_cli_log latest key convert-byron-key \
      --byron-signing-key-file "${STATE_CLUSTER}/byron/payment-keys.${index}.key" \
      --out-file "${STATE_CLUSTER}/byron/payment-keys.${index}-converted.skey" \
      --byron-payment-key-type

    cardano_cli_log latest key verification-key \
      --signing-key-file "${STATE_CLUSTER}/byron/payment-keys.${index}-converted.skey" \
      --verification-key-file "${STATE_CLUSTER}/byron/payment-keys.${index}-converted.vkey"

    cardano_cli_log latest address build \
      --testnet-magic "$NETWORK_MAGIC" \
      --payment-verification-key-file "${STATE_CLUSTER}/byron/payment-keys.${index}-converted.vkey" \
      > "${STATE_CLUSTER}/byron/address-${index}-converted"

    bft_port="$((NODE_PORT_BASE + (i - 1) * PORTS_PER_NODE ))"
    echo "$bft_port" > "${STATE_CLUSTER}/nodes/node-bft${i}/port"
  done
}

_create_pool_node_keys() {
  local pool_ix="${1:?}"

  mkdir -p "${STATE_CLUSTER}/nodes/node-pool${pool_ix}"
  echo "Generating Pool $pool_ix Secrets"

  cardano_cli_log latest address key-gen \
    --signing-key-file "${STATE_CLUSTER}/nodes/node-pool${pool_ix}/owner-utxo.skey" \
    --verification-key-file "${STATE_CLUSTER}/nodes/node-pool${pool_ix}/owner-utxo.vkey"
  cardano_cli_log latest stake-address key-gen \
    --signing-key-file "${STATE_CLUSTER}/nodes/node-pool${pool_ix}/owner-stake.skey" \
    --verification-key-file "${STATE_CLUSTER}/nodes/node-pool${pool_ix}/owner-stake.vkey"
  cardano_cli_log latest address build \
    --payment-verification-key-file "${STATE_CLUSTER}/nodes/node-pool${pool_ix}/owner-utxo.vkey" \
    --stake-verification-key-file "${STATE_CLUSTER}/nodes/node-pool${pool_ix}/owner-stake.vkey" \
    --testnet-magic "$NETWORK_MAGIC" \
    --out-file "${STATE_CLUSTER}/nodes/node-pool${pool_ix}/owner.addr"
  cardano_cli_log latest stake-address build \
    --stake-verification-key-file "${STATE_CLUSTER}/nodes/node-pool${pool_ix}/owner-stake.vkey" \
    --testnet-magic "$NETWORK_MAGIC" \
    --out-file "${STATE_CLUSTER}/nodes/node-pool${pool_ix}/owner-stake.addr"
  cardano_cli_log compatible shelley stake-address registration-certificate \
    --stake-verification-key-file "${STATE_CLUSTER}/nodes/node-pool${pool_ix}/owner-stake.vkey" \
    --out-file "${STATE_CLUSTER}/nodes/node-pool${pool_ix}/stake.reg.shelley.cert"
  cardano_cli_log conway stake-address registration-certificate \
    --stake-verification-key-file "${STATE_CLUSTER}/nodes/node-pool${pool_ix}/owner-stake.vkey" \
    --key-reg-deposit-amt "$KEY_DEPOSIT" \
    --out-file "${STATE_CLUSTER}/nodes/node-pool${pool_ix}/stake.reg.cert"

  cardano_cli_log latest stake-address key-gen \
    --signing-key-file "${STATE_CLUSTER}/nodes/node-pool${pool_ix}/reward.skey" \
    --verification-key-file "${STATE_CLUSTER}/nodes/node-pool${pool_ix}/reward.vkey"
  cardano_cli_log compatible shelley stake-address registration-certificate \
    --stake-verification-key-file "${STATE_CLUSTER}/nodes/node-pool${pool_ix}/reward.vkey" \
    --out-file "${STATE_CLUSTER}/nodes/node-pool${pool_ix}/stake-reward.reg.shelley.cert"
  cardano_cli_log conway stake-address vote-delegation-certificate \
    --stake-verification-key-file "${STATE_CLUSTER}/nodes/node-pool${pool_ix}/reward.vkey" \
    --always-abstain \
    --out-file "${STATE_CLUSTER}/nodes/node-pool${pool_ix}/stake-reward.vote_deleg.cert"
  cardano_cli_log conway stake-address registration-and-vote-delegation-certificate \
    --stake-verification-key-file "${STATE_CLUSTER}/nodes/node-pool${pool_ix}/reward.vkey" \
    --always-abstain \
    --key-reg-deposit-amt "$KEY_DEPOSIT" \
    --out-file "${STATE_CLUSTER}/nodes/node-pool${pool_ix}/stake-reward.reg.cert"

  cardano_cli_log latest node key-gen \
    --cold-verification-key-file "${STATE_CLUSTER}/nodes/node-pool${pool_ix}/cold.vkey" \
    --cold-signing-key-file "${STATE_CLUSTER}/nodes/node-pool${pool_ix}/cold.skey" \
    --operational-certificate-issue-counter-file "${STATE_CLUSTER}/nodes/node-pool${pool_ix}/cold.counter"
  cardano_cli_log latest node key-gen-KES \
    --verification-key-file "${STATE_CLUSTER}/nodes/node-pool${pool_ix}/kes.vkey" \
    --signing-key-file "${STATE_CLUSTER}/nodes/node-pool${pool_ix}/kes.skey"
  cardano_cli_log latest node key-gen-VRF \
    --verification-key-file "${STATE_CLUSTER}/nodes/node-pool${pool_ix}/vrf.vkey" \
    --signing-key-file "${STATE_CLUSTER}/nodes/node-pool${pool_ix}/vrf.skey"

  cardano_cli_log compatible shelley stake-address stake-delegation-certificate \
    --stake-verification-key-file "${STATE_CLUSTER}/nodes/node-pool${pool_ix}/owner-stake.vkey" \
    --cold-verification-key-file  "${STATE_CLUSTER}/nodes/node-pool${pool_ix}/cold.vkey" \
    --out-file "${STATE_CLUSTER}/nodes/node-pool${pool_ix}/owner-stake.deleg.shelley.cert"
  cardano_cli_log conway stake-address stake-delegation-certificate \
    --stake-verification-key-file "${STATE_CLUSTER}/nodes/node-pool${pool_ix}/owner-stake.vkey" \
    --cold-verification-key-file  "${STATE_CLUSTER}/nodes/node-pool${pool_ix}/cold.vkey" \
    --out-file "${STATE_CLUSTER}/nodes/node-pool${pool_ix}/owner-stake.deleg.cert"

  cardano_cli_log latest node issue-op-cert \
    --kes-period 0 \
    --cold-signing-key-file "${STATE_CLUSTER}/nodes/node-pool${pool_ix}/cold.skey" \
    --kes-verification-key-file "${STATE_CLUSTER}/nodes/node-pool${pool_ix}/kes.vkey" \
    --operational-certificate-issue-counter-file "${STATE_CLUSTER}/nodes/node-pool${pool_ix}/cold.counter" \
    --out-file "${STATE_CLUSTER}/nodes/node-pool${pool_ix}/op.cert"
}

_create_pool_registration() {
  local pool_ix="${1:?}"
  local metadata_url="http://localhost:${WEBSERVER_PORT}/pool${pool_ix}.json"
  local metadata_hash pool_port

  metadata_hash="$(cardano_cli_log latest stake-pool metadata-hash --pool-metadata-file \
    "${STATE_CLUSTER}/webserver/pool${pool_ix}.json")"
  pool_port="$((NODE_PORT_BASE + (NUM_BFT_NODES + pool_ix - 1) * PORTS_PER_NODE))"
  echo "$pool_port" > "${STATE_CLUSTER}/nodes/node-pool${pool_ix}/port"
  echo "$POOL_PLEDGE" > "${STATE_CLUSTER}/nodes/node-pool${pool_ix}/pledge"

  cardano_cli_log compatible shelley stake-pool registration-certificate \
    --cold-verification-key-file "${STATE_CLUSTER}/nodes/node-pool${pool_ix}/cold.vkey" \
    --vrf-verification-key-file "${STATE_CLUSTER}/nodes/node-pool${pool_ix}/vrf.vkey" \
    --pool-pledge "$POOL_PLEDGE" \
    --pool-margin 0.35 \
    --pool-cost "$POOL_COST" \
    --pool-reward-account-verification-key-file "${STATE_CLUSTER}/nodes/node-pool${pool_ix}/reward.vkey" \
    --pool-owner-stake-verification-key-file "${STATE_CLUSTER}/nodes/node-pool${pool_ix}/owner-stake.vkey" \
    --metadata-url "$metadata_url" \
    --metadata-hash "$metadata_hash" \
    --pool-relay-port "$pool_port" \
    --pool-relay-ipv4 "127.0.0.1" \
    --testnet-magic "$NETWORK_MAGIC" \
    --out-file "${STATE_CLUSTER}/nodes/node-pool${pool_ix}/register.shelley.cert"
  cardano_cli_log conway stake-pool registration-certificate \
    --cold-verification-key-file "${STATE_CLUSTER}/nodes/node-pool${pool_ix}/cold.vkey" \
    --vrf-verification-key-file "${STATE_CLUSTER}/nodes/node-pool${pool_ix}/vrf.vkey" \
    --pool-pledge "$POOL_PLEDGE" \
    --pool-margin 0.35 \
    --pool-cost "$POOL_COST" \
    --pool-reward-account-verification-key-file "${STATE_CLUSTER}/nodes/node-pool${pool_ix}/reward.vkey" \
    --pool-owner-stake-verification-key-file "${STATE_CLUSTER}/nodes/node-pool${pool_ix}/owner-stake.vkey" \
    --metadata-url "$metadata_url" \
    --metadata-hash "$metadata_hash" \
    --pool-relay-port "$pool_port" \
    --pool-relay-ipv4 "127.0.0.1" \
    --testnet-magic "$NETWORK_MAGIC" \
    --out-file "${STATE_CLUSTER}/nodes/node-pool${pool_ix}/register.cert"
}

create_pools_files() {
  local i
  for i in $(seq 1 "$NUM_POOLS"); do
    _create_pool_node_keys "$i"
    create_pool_metadata "$i"
    _create_pool_registration "$i"
  done

  mv "${STATE_CLUSTER}/shelley/utxo-keys/utxo1.vkey" "${STATE_CLUSTER}/shelley/genesis-utxo.vkey"
  mv "${STATE_CLUSTER}/shelley/utxo-keys/utxo1.skey" "${STATE_CLUSTER}/shelley/genesis-utxo.skey"
  rmdir "${STATE_CLUSTER}/shelley/utxo-keys"
}

create_byron_update_proposal() {
  local proposal_file="${1:?Proposal file argument is required}"
  local protocol_major="${2:?Protocol major version argument is required}"
  local protocol_minor="${3:?Protocol minor version argument is required}"

  cardano_cli_log byron governance create-update-proposal \
    --filepath "$proposal_file" \
    --testnet-magic "$NETWORK_MAGIC" \
    --signing-key "${STATE_CLUSTER}/nodes/node-bft1/byron-deleg.key" \
    --protocol-version-major "$protocol_major" \
    --protocol-version-minor "$protocol_minor" \
    --protocol-version-alt 0 \
    --application-name "cardano-sl" \
    --software-version-num 1 \
    --system-tag "linux" \
    --installer-hash 0
}

submit_byron_proposal_with_votes() {
  local proposal_file="${1:?Proposal file argument is required}"
  local vote_base="${2:?Vote base argument is required}"
  local i

  cardano_cli_log byron governance submit-update-proposal \
    --testnet-magic "$NETWORK_MAGIC" \
    --filepath "$proposal_file"
  sleep 5

  for i in $(seq 1 "$NUM_BFT_NODES"); do
    cardano_cli_log byron governance create-proposal-vote \
      --proposal-filepath "$proposal_file" \
      --testnet-magic "$NETWORK_MAGIC" \
      --signing-key "${STATE_CLUSTER}/nodes/node-bft${i}/byron-deleg.key" \
      --vote-yes \
      --output-filepath "${vote_base}-${i}.vote"

    cardano_cli_log byron governance submit-proposal-vote  \
      --testnet-magic "$NETWORK_MAGIC" \
      --filepath "${vote_base}-${i}.vote"
  done
  sleep 5
}

get_shelley_env() {
  local vkey skey

  GENESIS_VERIFICATION=()
  for vkey in "$STATE_CLUSTER"/shelley/genesis-keys/genesis?.vkey; do
    [ -e "$vkey" ] || { echo "No genesis verification keys found in ${STATE_CLUSTER}/shelley/genesis-keys, line $LINENO in ${BASH_SOURCE[0]}" >&2; exit 1; }
    GENESIS_VERIFICATION+=("--genesis-verification-key-file" "$vkey")
  done
  readonly GENESIS_VERIFICATION

  GENESIS_SIGNING=()
  for skey in "$STATE_CLUSTER"/shelley/genesis-keys/genesis?.skey; do
    [ -e "$skey" ] || { echo "No genesis signing keys found in ${STATE_CLUSTER}/shelley/genesis-keys, line $LINENO in ${BASH_SOURCE[0]}" >&2; exit 1; }
    GENESIS_SIGNING+=("--signing-key-file" "$skey")
  done
  readonly GENESIS_SIGNING

  DELEGATE_SIGNING=()
  for skey in "$STATE_CLUSTER"/shelley/delegate-keys/delegate?.skey; do
    [ -e "$skey" ] || { echo "No delegate signing keys found in ${STATE_CLUSTER}/shelley/delegate-keys, line $LINENO in ${BASH_SOURCE[0]}" >&2; exit 1; }
    DELEGATE_SIGNING+=("--signing-key-file" "$skey")
  done
  readonly DELEGATE_SIGNING

  FAUCET_ADDR="$(<"$STATE_CLUSTER"/byron/address-000-converted)"
  readonly FAUCET_ADDR
  readonly FAUCET_SKEY="${STATE_CLUSTER}/byron/payment-keys.000-converted.skey"
}

create_legacy_update_proposal() {
  local era="${1:?}"
  local proposal_file="${2:?}"
  local major_version="${3:?}"
  shift 3

  cardano_cli_log compatible "$era" governance action create-protocol-parameters-update \
    --out-file "$proposal_file" \
    --epoch "$(get_epoch)" \
    "${GENESIS_VERIFICATION[@]}" \
    --protocol-major-version "$major_version" \
    --protocol-minor-version 0 \
    "$@"
}

submit_legacy_update_proposal() {
  local era="${1:?}"
  local proposal_file="${2:?}"
  local tx_base="${3:?}"

  get_txins "$FAUCET_ADDR" "$FEE"

  local txout_amount="$((TXIN_AMOUNT - FEE))"

  cardano_cli_log compatible "$era" transaction signed-transaction \
    --fee    "$FEE" \
    "${TXINS[@]}" \
    --tx-out "${FAUCET_ADDR}+${txout_amount}" \
    --update-proposal-file "$proposal_file" \
    --signing-key-file "$FAUCET_SKEY" \
    "${DELEGATE_SIGNING[@]}" \
    --testnet-magic    "$NETWORK_MAGIC" \
    --out-file         "${tx_base}-tx.tx"

  cardano_cli_log latest transaction submit \
    --tx-file "${tx_base}-tx.tx" \
    --testnet-magic "$NETWORK_MAGIC"

  sleep "$SUBMIT_DELAY"
  if ! check_spend_success "${TXINS[@]}"; then
    echo "Failed to spend Tx inputs, line $LINENO in ${BASH_SOURCE[0]}" >&2
    exit 1
  fi
}

do_legacy_hf() {
  local from_era="${1:?}"
  local to_era="${2:?}"
  local major_version="${3:?}"
  local wait_epoch="${4:?}"
  shift 4

  local proposal_file="${STATE_CLUSTER}/shelley/update-proposal-${to_era,,}.proposal"
  local tx_base="${STATE_CLUSTER}/shelley/update-proposal-${to_era,,}"

  sleep "$PROPOSAL_DELAY"
  echo "Submitting update proposal to transfer to $to_era"

  create_legacy_update_proposal "$from_era" "$proposal_file" "$major_version" "$@"
  submit_legacy_update_proposal "$from_era" "$proposal_file" "$tx_base"

  echo "Waiting for $to_era era to start"
  wait_for_epoch "$wait_epoch"
}

_register_governance_in_conway() {
  local deposits="$((KEY_DEPOSIT + DREP_DEPOSIT))"
  local needed_amount="$(( (DREP_DELEGATED + deposits) * NUM_DREPS ))"
  local stop_txin_amount="$((FEE + needed_amount))"
  local f i

  get_txins "$FAUCET_ADDR" "$stop_txin_amount"

  local txout_amount="$((TXIN_AMOUNT - stop_txin_amount))"
  local v9_tx="${STATE_CLUSTER}/governance_data/setup_governance"

  local -a cc_args=()
  for f in "$STATE_CLUSTER"/governance_data/cc_member*_committee_hot_auth.cert; do
    [ -e "$f" ] || continue
    cc_args+=( "--certificate-file" "$f" )
  done

  local -a cc_signing=()
  for f in "$STATE_CLUSTER"/governance_data/cc_member*_committee_cold.skey; do
    [ -e "$f" ] || continue
    cc_signing+=( "--signing-key-file" "$f" )
  done

  local -a dreps_args=()
  local -a dreps_signing=()
  for i in $(seq 1 "$NUM_DREPS"); do
    dreps_args+=( \
      "--tx-out" "$(<"${STATE_CLUSTER}/governance_data/vote_stake_addr${i}.addr")+${DREP_DELEGATED}" \
      "--certificate-file" "${STATE_CLUSTER}/governance_data/default_drep_${i}_drep_reg.cert" \
      "--certificate-file" "${STATE_CLUSTER}/governance_data/vote_stake_addr${i}_stake.reg.cert" \
      "--certificate-file" "${STATE_CLUSTER}/governance_data/vote_stake_addr${i}_stake.vote_deleg.cert" \
    )
    dreps_signing+=( \
      "--signing-key-file" "${STATE_CLUSTER}/governance_data/default_drep_${i}_drep.skey" \
      "--signing-key-file" "${STATE_CLUSTER}/governance_data/vote_stake_addr${i}.skey" \
      "--signing-key-file" "${STATE_CLUSTER}/governance_data/vote_stake_addr${i}_stake.skey" \
    )
  done

  local -a pool_args=()
  local -a pool_signing=()
  for i in $(seq 1 "$NUM_POOLS"); do
    pool_args+=( \
      "--certificate-file" "${STATE_CLUSTER}/nodes/node-pool${i}/stake-reward.vote_deleg.cert" \
    )
    pool_signing+=( \
      "--signing-key-file" "${STATE_CLUSTER}/nodes/node-pool${i}/reward.skey" \
    )
  done

  cardano_cli_log conway transaction build-raw \
    --fee    "$FEE" \
    "${TXINS[@]}" \
    "${cc_args[@]}" \
    "${dreps_args[@]}" \
    "${pool_args[@]}" \
    --tx-out "${FAUCET_ADDR}+${txout_amount}" \
    --out-file "${v9_tx}-tx.txbody"

  cardano_cli_log conway transaction sign \
    --signing-key-file "$FAUCET_SKEY" \
    "${cc_signing[@]}" \
    "${dreps_signing[@]}" \
    "${pool_signing[@]}" \
    --testnet-magic    "$NETWORK_MAGIC" \
    --tx-body-file     "${v9_tx}-tx.txbody" \
    --out-file         "${v9_tx}-tx.tx"

  cardano_cli_log conway transaction submit \
    --tx-file "${v9_tx}-tx.tx" \
    --testnet-magic "$NETWORK_MAGIC"

  sleep "$SUBMIT_DELAY"
  if ! check_spend_success "${TXINS[@]}"; then
    echo "Failed to spend Tx inputs, line $LINENO in ${BASH_SOURCE[0]}" >&2
    exit 1
  fi

  if ! is_truthy "${NO_CC:-}"; then
    local cc_size
    cc_size="$(cardano-cli conway query committee-state --active --testnet-magic "$NETWORK_MAGIC" |
      grep -c '"status": "Active"')"
    [ "$cc_size" -ge "$NUM_CC" ] || \
      { echo "The CC members were not registered, line $LINENO in ${BASH_SOURCE[0]}" >&2; exit 1; }
  fi
}

submit_gov_action() {
  local action_base="${1:?}"
  local stop_txin_amount="$((FEE + GOV_ACTION_DEPOSIT))"

  get_txins "$FAUCET_ADDR" "$stop_txin_amount"

  local txout_amount="$((TXIN_AMOUNT - stop_txin_amount))"

  cardano_cli_log conway transaction build-raw \
    --fee    "$FEE" \
    "${TXINS[@]}" \
    --proposal-file "${action_base}.action" \
    --tx-out "${FAUCET_ADDR}+${txout_amount}" \
    --out-file "${action_base}-tx.txbody"

  cardano_cli_log conway transaction sign \
    --signing-key-file "$FAUCET_SKEY" \
    --testnet-magic    "$NETWORK_MAGIC" \
    --tx-body-file     "${action_base}-tx.txbody" \
    --out-file         "${action_base}-tx.tx"

  cardano_cli_log conway transaction submit \
    --tx-file "${action_base}-tx.tx" \
    --testnet-magic "$NETWORK_MAGIC"

  sleep "$SUBMIT_DELAY"
  if ! check_spend_success "${TXINS[@]}"; then
    echo "Failed to spend Tx inputs, line $LINENO in ${BASH_SOURCE[0]}" >&2
    exit 1
  fi
}

create_and_submit_hf_action() {
  local hf_action="${1:?}"
  local era="${2:?}"
  local major_version="${3:?}"
  local prev_txid="${4:-}"
  local prev_index="${5:-0}"

  local -a prev_args=()
  if [ -n "$prev_txid" ]; then
    prev_args=( \
      "--prev-governance-action-tx-id" "$prev_txid" \
      "--prev-governance-action-index" "$prev_index" \
    )
  fi

  cardano_cli_log "$era" governance action create-hardfork \
    --testnet \
    --governance-action-deposit "$GOV_ACTION_DEPOSIT" \
    --deposit-return-stake-verification-key-file "${STATE_CLUSTER}/nodes/node-pool1/reward.vkey" \
    "${prev_args[@]}" \
    --anchor-url "http://www.hardfork-pv${major_version}.com" \
    --anchor-data-hash 5d372dca1a4cc90d7d16d966c48270e33e3aa0abcb0e78f0d5ca7ff330d2245d \
    --protocol-major-version "$major_version" \
    --protocol-minor-version 0 \
    --out-file "${hf_action}.action"

  submit_gov_action "$hf_action"
}

_update_cost_model_in_pv9() {
  echo "Submitting cost model update proposal"
  local cost_model_base="${STATE_CLUSTER}/governance_data/cost_model_conway"
  local cost_model_action="${cost_model_base}_action"

  cardano_cli_log conway governance action create-protocol-parameters-update \
    --testnet \
    --governance-action-deposit "$GOV_ACTION_DEPOSIT" \
    --deposit-return-stake-verification-key-file "${STATE_CLUSTER}/nodes/node-pool1/reward.vkey" \
    --anchor-url "http://www.cost-model-update.com" \
    --anchor-data-hash 5d372dca1a4cc90d7d16d966c48270e33e3aa0abcb0e78f0d5ca7ff330d2245d \
    --cost-model-file "${STATE_CLUSTER}/shelley/cost_models_list.json" \
    --out-file "${cost_model_action}.action"

  submit_gov_action "$cost_model_action"

  local cost_model_txid
  cost_model_txid="$(cardano_cli_log conway transaction txid \
    --output-text --tx-body-file "${cost_model_action}-tx.txbody")"
  vote_on_action "$cost_model_txid" "$cost_model_action"
  submit_votes "${cost_model_base}_votes" "$cost_model_action"
}

_wait_pv9_cost_model_enacted() {
  local vote_epoch="${1:?}"

  echo "Waiting for cost model update to get enacted"
  wait_for_epoch "$((vote_epoch + 2))"

  save_protocol_params "$PPARAMS_FILE"
  local plutusv2_len
  plutusv2_len="$(jq ".costModels.PlutusV2 | length" < "$PPARAMS_FILE")"
  if [ "$plutusv2_len" != 185 ]; then
    echo "Unexpected PlutusV2 cost model length '$plutusv2_len' on line $LINENO in ${BASH_SOURCE[0]}" >&2
    exit 1
  fi
}

vote_on_action() {
  local action_txid="${1:?}"
  local action_base="${2:?}"
  local with_spos="${3:-no}"
  local with_dreps="${4:-no}"
  local f

  echo "Voting on $(basename "$action_base") proposal"

  local index=0
  for f in "$STATE_CLUSTER"/governance_data/cc_member*_committee_hot.vkey; do
    [ -e "$f" ] || continue
    index="$((index + 1))"
    cardano_cli_log conway governance vote create \
      --yes \
      --governance-action-tx-id "$action_txid" \
      --governance-action-index 0 \
      --cc-hot-verification-key-file "$f" \
      --out-file "${action_base}_cc${index}.vote"
  done

  if [ "$with_spos" = "yes" ]; then
    index=0
    for f in "$STATE_CLUSTER"/nodes/node-pool*/cold.vkey; do
      [ -e "$f" ] || continue
      index="$((index + 1))"
      cardano_cli_log conway governance vote create \
        --yes \
        --governance-action-tx-id "$action_txid" \
        --governance-action-index 0 \
        --cold-verification-key-file "$f" \
        --out-file "${action_base}_spo${index}.vote"
    done
  fi

  if [ "$with_dreps" = "yes" ]; then
    index=0
    for f in "$STATE_CLUSTER"/governance_data/default_drep*_drep.vkey; do
      [ -e "$f" ] || continue
      index="$((index + 1))"
      cardano_cli_log conway governance vote create \
        --yes \
        --governance-action-tx-id "$action_txid" \
        --governance-action-index 0 \
        --drep-verification-key-file "$f" \
        --out-file "${action_base}_drep${index}.vote"
    done
  fi
}

submit_votes() {
  local votes_base="${1:?}"
  local action_base="${2:?}"
  local f

  local -a vote_files=()
  for f in "$action_base"_*.vote; do
    [ -e "$f" ] || continue
    vote_files+=( "--vote-file" "$f" )
  done

  local -a cc_signing=()
  for f in "$STATE_CLUSTER"/governance_data/cc_member*_committee_hot.skey; do
    [ -e "$f" ] || continue
    cc_signing+=( "--signing-key-file" "$f" )
  done

  local -a pool_signing=()
  if [ -e "${action_base}_spo1.vote" ]; then
    for f in "$STATE_CLUSTER"/nodes/node-pool*/cold.skey; do
      [ -e "$f" ] || continue
      pool_signing+=( "--signing-key-file" "$f" )
    done
  fi

  local -a drep_signing=()
  if [ -e "${action_base}_drep1.vote" ]; then
    for f in "$STATE_CLUSTER"/governance_data/default_drep*_drep.skey; do
      [ -e "$f" ] || continue
      drep_signing+=( "--signing-key-file" "$f" )
    done
  fi

  get_txins "$FAUCET_ADDR" "$FEE"

  local txout_amount="$((TXIN_AMOUNT - FEE))"

  cardano_cli_log conway transaction build-raw \
    --fee    "$FEE" \
    "${TXINS[@]}" \
    "${vote_files[@]}" \
    --tx-out "${FAUCET_ADDR}+${txout_amount}" \
    --out-file "${votes_base}-tx.txbody"

  cardano_cli_log conway transaction sign \
    --signing-key-file "$FAUCET_SKEY" \
    "${cc_signing[@]}" \
    "${pool_signing[@]}" \
    "${drep_signing[@]}" \
    --testnet-magic    "$NETWORK_MAGIC" \
    --tx-body-file     "${votes_base}-tx.txbody" \
    --out-file         "${votes_base}-tx.tx"

  cardano_cli_log conway transaction submit \
    --tx-file "${votes_base}-tx.tx" \
    --testnet-magic "$NETWORK_MAGIC"

  sleep "$SUBMIT_DELAY"
  if ! check_spend_success "${TXINS[@]}"; then
    echo "Failed to spend Tx inputs, line $LINENO in ${BASH_SOURCE[0]}" >&2
    exit 1
  fi
}

submit_byron_genesis_txs() {
  echo "Sleeping for initial Tx submission delay of $TX_SUBMISSION_DELAY seconds"
  sleep "$TX_SUBMISSION_DELAY"

  echo "Moving funds out of Byron genesis"
  local i
  for i in $(seq 1 "$NUM_BFT_NODES"); do
    cardano_cli_log byron transaction submit-tx \
      --testnet-magic "$NETWORK_MAGIC" \
      --tx "${STATE_CLUSTER}/byron/tx${i}.tx"
  done

  echo "Waiting for next Byron epoch to start"
  sleep "$((200 - TX_SUBMISSION_DELAY + 5))"
}

hf_to_byron_pv1() {
  local byron_v1_proposal="${STATE_CLUSTER}/byron/update-proposal-byron-v1.proposal"

  create_byron_update_proposal "$byron_v1_proposal" 1 0
  submit_byron_proposal_with_votes "$byron_v1_proposal" "${STATE_CLUSTER}/byron/update-proposal-byron-v1"

  echo "Waiting for Byron era with PV1 to start"
  sleep 200
}

hf_to_shelley() {
  local shelley_proposal="${STATE_CLUSTER}/byron/update-proposal-shelley.proposal"

  create_byron_update_proposal "$shelley_proposal" 2 0
  submit_byron_proposal_with_votes "$shelley_proposal" "${STATE_CLUSTER}/byron/update-proposal-shelley"

  local conf
  for conf in "$STATE_CLUSTER"/config-*.json; do
    [ -e "$conf" ] || { echo "No config files found in ${STATE_CLUSTER}, line $LINENO in ${BASH_SOURCE[0]}" >&2; exit 1; }
    local fname="${conf##*/}"
    jq '
      ."LastKnownBlockVersion-Major" = 2' \
      "$conf" > "${STATE_CLUSTER}/${fname}_jq"
    cat "${STATE_CLUSTER}/${fname}_jq" > "${STATE_CLUSTER}/${fname}"
    rm -f "${STATE_CLUSTER}/${fname}_jq"
  done

  supervisorctl -s unix:///"$SUPERVISORD_SOCKET_PATH" restart nodes:

  echo "Waiting for Shelley era to start"
  sleep 190
  wait_for_era "Shelley"
}

hf_to_allegra() {
  local cur_epoch="${1:?}"

  sleep "$PROPOSAL_DELAY"
  echo "Submitting update proposal to transfer to Allegra, transferring funds to pool owners, registering pools and delegations"

  local allegra_proposal_file="${STATE_CLUSTER}/shelley/update-proposal-allegra.proposal"
  local allegra_tx_base="${STATE_CLUSTER}/shelley/transfer-register-delegate"

  cardano_cli_log compatible shelley governance action create-protocol-parameters-update \
    --out-file "$allegra_proposal_file" \
    --epoch "$(get_epoch)" \
    "${GENESIS_VERIFICATION[@]}" \
    --protocol-major-version 3 \
    --protocol-minor-version 0

  local deposits="$((POOL_DEPOSIT + (2 * KEY_DEPOSIT) ))"
  local needed_amount="$(( (POOL_PLEDGE + deposits) * NUM_POOLS ))"
  local stop_txin_amount="$((needed_amount + FEE))"

  get_txins "$FAUCET_ADDR" "$stop_txin_amount"

  local txout_amount="$((TXIN_AMOUNT - stop_txin_amount))"

  local -a pool_args=()
  local -a pool_signing=()
  local i
  for i in $(seq 1 "$NUM_POOLS"); do
    pool_args+=( \
      "--tx-out" "$(<"${STATE_CLUSTER}/nodes/node-pool${i}/owner.addr")+${POOL_PLEDGE}" \
      "--certificate-file" "${STATE_CLUSTER}/nodes/node-pool${i}/stake.reg.shelley.cert" \
      "--certificate-file" "${STATE_CLUSTER}/nodes/node-pool${i}/stake-reward.reg.shelley.cert" \
      "--certificate-file" "${STATE_CLUSTER}/nodes/node-pool${i}/register.shelley.cert" \
      "--certificate-file" "${STATE_CLUSTER}/nodes/node-pool${i}/owner-stake.deleg.shelley.cert" \
    )
    pool_signing+=( \
      "--signing-key-file" "${STATE_CLUSTER}/nodes/node-pool${i}/owner-stake.skey" \
      "--signing-key-file" "${STATE_CLUSTER}/nodes/node-pool${i}/reward.skey" \
      "--signing-key-file" "${STATE_CLUSTER}/nodes/node-pool${i}/cold.skey" \
    )
  done

  cardano_cli_log compatible shelley transaction signed-transaction \
    --fee    "$FEE" \
    "${TXINS[@]}" \
    --tx-out "${FAUCET_ADDR}+${txout_amount}" \
    "${pool_args[@]}" \
    --update-proposal-file "$allegra_proposal_file" \
    "${pool_signing[@]}" \
    "${GENESIS_SIGNING[@]}" \
    "${DELEGATE_SIGNING[@]}" \
    --signing-key-file "$FAUCET_SKEY" \
    --testnet-magic    "$NETWORK_MAGIC" \
    --out-file         "${allegra_tx_base}-tx.tx"

  cardano_cli_log latest transaction submit \
    --tx-file "${allegra_tx_base}-tx.tx" \
    --testnet-magic "$NETWORK_MAGIC"

  sleep "$SUBMIT_DELAY"
  if ! check_spend_success "${TXINS[@]}"; then
    echo "Failed to spend Tx inputs, line $LINENO in ${BASH_SOURCE[0]}" >&2
    exit 1
  fi

  echo "Waiting for Allegra era to start"
  wait_for_epoch "$((cur_epoch + 1))"
  wait_for_era "Allegra"
}

hf_to_mary() {
  local cur_epoch="${1:?}"
  do_legacy_hf "allegra" "Mary" 4 "$((cur_epoch + 1))" --decentralization-parameter 0
  wait_for_era "Mary"
}

hf_to_alonzo() {
  if [ "$PROTOCOL_VERSION" -lt 5 ]; then
    echo "Skipping HF to Alonzo as PROTOCOL_VERSION is set to $PROTOCOL_VERSION"
    return
  fi

  local cur_epoch="${1:?}"
  do_legacy_hf "mary" "Alonzo" 5 "$((cur_epoch + 1))"
  wait_for_era "Alonzo"
}

hf_to_alonzo_pv6() {
  if [ "$PROTOCOL_VERSION" -lt 6 ]; then
    echo "Skipping HF to Alonzo PV6 as PROTOCOL_VERSION is set to $PROTOCOL_VERSION"
    return
  fi

  local cur_epoch="${1:?}"

  do_legacy_hf "alonzo" "Alonzo-PV6" 6 "$((cur_epoch + 1))"

  if save_protocol_params "$PPARAMS_FILE"; then
    local cur_protver
    cur_protver="$(jq ".protocolVersion.major" < "$PPARAMS_FILE")"
    [ "$cur_protver" = 6 ] || { echo "Unexpected protocol version '$cur_protver' on line $LINENO in ${BASH_SOURCE[0]}" >&2; exit 1; }
  fi
}

hf_to_babbage() {
  if [ "$PROTOCOL_VERSION" -lt 7 ]; then
    echo "Skipping HF to Babbage as PROTOCOL_VERSION is set to $PROTOCOL_VERSION"
    return
  fi

  local cur_epoch="${1:?}"

  do_legacy_hf "alonzo" "Babbage" 7 "$((cur_epoch + 1))"
  wait_for_era "Babbage"
}

hf_to_babbage_pv8() {
  if [ "$PROTOCOL_VERSION" -lt 8 ]; then
    echo "Skipping HF to Babbage PV8 as PROTOCOL_VERSION is set to $PROTOCOL_VERSION"
    return
  fi

  local cur_epoch="${1:?}"

  do_legacy_hf "babbage" "Babbage-PV8" 8 "$((cur_epoch + 1))"

  if save_protocol_params "$PPARAMS_FILE"; then
    local cur_protver
    cur_protver="$(jq ".protocolVersion.major" < "$PPARAMS_FILE")"
    [ "$cur_protver" = 8 ] || { echo "Unexpected protocol version '$cur_protver' on line $LINENO in ${BASH_SOURCE[0]}" >&2; exit 1; }
  fi
}

hf_to_conway() {
  if [ "$PROTOCOL_VERSION" -lt 9 ]; then
    echo "Skipping HF to Conway as PROTOCOL_VERSION is set to $PROTOCOL_VERSION"
    return
  fi

  local cur_epoch="${1:?}"

  do_legacy_hf "babbage" "Conway" 9 "$((cur_epoch + 1))"
  wait_for_era "Conway"

  save_protocol_params "$PPARAMS_FILE"
  local cur_protver
  cur_protver="$(jq ".protocolVersion.major" < "$PPARAMS_FILE")"
  [ "$cur_protver" = 9 ] || { echo "Unexpected protocol version '$cur_protver' on line $LINENO in ${BASH_SOURCE[0]}" >&2; exit 1; }

  _register_governance_in_conway
}

hf_to_conway_pv10() {
  local cur_epoch="${1:?}"

  _update_cost_model_in_pv9

  if [ "$PROTOCOL_VERSION" -lt 10 ]; then
    echo "Skipping HF to Conway PV10 as PROTOCOL_VERSION is set to $PROTOCOL_VERSION"
    _wait_pv9_cost_model_enacted "$cur_epoch"
    return
  fi

  wait_for_epoch "$((cur_epoch + 1))"

  echo "Submitting hard fork proposal to update to Conway PV10"
  local pv10_hf_base="${STATE_CLUSTER}/governance_data/hardfork_pv10"
  local pv10_hf_action="${pv10_hf_base}_action"

  create_and_submit_hf_action "$pv10_hf_action" "conway" 10

  PV10_HF_ACTION_TXID="$(cardano_cli_log conway transaction txid \
    --output-text --tx-body-file "${pv10_hf_action}-tx.txbody")"
  readonly PV10_HF_ACTION_TXID

  vote_on_action "$PV10_HF_ACTION_TXID" "$pv10_hf_action" "yes"
  submit_votes "${pv10_hf_base}_votes" "$pv10_hf_action"

  _wait_pv9_cost_model_enacted "$cur_epoch"

  echo "Waiting for Conway PV10 to start"
  wait_for_epoch "$((cur_epoch + 3))"

  save_protocol_params "$PPARAMS_FILE"
  local cur_protver
  cur_protver="$(jq ".protocolVersion.major" < "$PPARAMS_FILE")"
  [ "$cur_protver" = 10 ] || { echo "Unexpected protocol version '$cur_protver' on line $LINENO in ${BASH_SOURCE[0]}" >&2; exit 1; }
}

hf_to_conway_pv11() {
  if [ "$PROTOCOL_VERSION" -lt 11 ]; then
    echo "Skipping HF to Conway PV11 as PROTOCOL_VERSION is set to $PROTOCOL_VERSION"
    return
  fi

  local cur_epoch="${1:?}"

  echo "Submitting hard fork proposal to update to Conway PV11"

  local pv11_hf_base="${STATE_CLUSTER}/governance_data/hardfork_pv11_action"
  local pv11_hf_action="${pv11_hf_base}_action"

  create_and_submit_hf_action "$pv11_hf_action" "conway" 11 "${PV10_HF_ACTION_TXID:?"Must exist"}"

  PV11_HF_ACTION_TXID="$(cardano_cli_log conway transaction txid \
    --output-text --tx-body-file "${pv11_hf_action}-tx.txbody")"
  readonly PV11_HF_ACTION_TXID

  vote_on_action "$PV11_HF_ACTION_TXID" "$pv11_hf_action" "yes" "yes"
  submit_votes "${pv11_hf_base}_votes" "$pv11_hf_action"

  echo "Waiting for Conway PV11 to start"
  wait_for_epoch "$((cur_epoch + 2))"

  save_protocol_params "$PPARAMS_FILE"
  local cur_protver
  cur_protver="$(jq ".protocolVersion.major" < "$PPARAMS_FILE")"
  [ "$cur_protver" = 11 ] || { echo "Unexpected protocol version '$cur_protver' on line $LINENO in ${BASH_SOURCE[0]}" >&2; exit 1; }
}

main() {
  initialize_globals
  setup_state_cluster "${STATE_CLUSTER}/shelley"
  configure_supervisor
  create_genesis
  create_committee_keys_in_genesis
  get_genesis_data
  edit_node_configs
  create_bft_nodes_files
  create_pools_files
  create_dreps_files
  create_cluster_scripts
  get_shelley_env

  start_cluster_nodes

  submit_byron_genesis_txs
  hf_to_byron_pv1
  hf_to_shelley

  start_optional_services

  hf_to_allegra "$(get_epoch)"
  hf_to_mary "$(get_epoch)"
  hf_to_alonzo "$(get_epoch)"
  hf_to_alonzo_pv6 "$(get_epoch)"
  hf_to_babbage "$(get_epoch)"
  hf_to_babbage_pv8 "$(get_epoch)"
  hf_to_conway "$(get_epoch)"
  hf_to_conway_pv10 "$(get_epoch)"
  hf_to_conway_pv11 "$(get_epoch)"

  : > "$START_CLUSTER_STATUS"
  echo "Cluster started 🚀"
}

main "$@"
