#!/usr/bin/env bash

# Requires `docker`, `kurtosis`, `yq`, `curl`, `jq`

set -Eeuo pipefail

SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
NETWORK_PARAMS_FILE=$SCRIPT_DIR/network_params.yaml
BEHAVIOR=$1
ENCLAVE_NAME=local-testnet-$BEHAVIOR

SLOT_DURATION_MS=$(yq eval ".network_params.slot_duration_ms" $NETWORK_PARAMS_FILE)
KEYS_PER_NODE=$(yq eval ".network_params.num_validator_keys_per_node" $NETWORK_PARAMS_FILE)
LH_IMAGE_NAME=$(yq eval ".participants[0].cl_image" $NETWORK_PARAMS_FILE)

if [[ "$BEHAVIOR" != "success" ]] && [[ "$BEHAVIOR" != "failure" ]]; then
    echo "Usage: doppelganger_protection.sh [success|failure]"
    exit 1
fi

function exit_and_dump_logs() {
    local exit_code=$1
    echo "Shutting down..."
    $SCRIPT_DIR/../local_testnet/stop_local_testnet.sh $ENCLAVE_NAME
    echo "Test completed with exit code $exit_code."
    exit $exit_code
}

function get_service_status() {
    local service_name=$1
    kurtosis service inspect $ENCLAVE_NAME $service_name | grep Status | cut -d':' -f2 | xargs
}

function run_command_without_exit() {
    local command=$1
    set +e
    eval "$command"
    local exit_code=$?
    set -e
    echo $exit_code
}

# Start local testnet
$SCRIPT_DIR/../local_testnet/start_local_testnet.sh -e $ENCLAVE_NAME -b false -c -n $NETWORK_PARAMS_FILE

# Immediately stop node 4 (as we only need the node 4 validator keys generated for later use)
kurtosis service stop $ENCLAVE_NAME cl-4-lighthouse-geth el-4-geth-lighthouse vc-4-geth-lighthouse > /dev/null

# Get the http port to get the config
BN1_HTTP_ADDRESS=`kurtosis port print $ENCLAVE_NAME cl-1-lighthouse-geth http`

# Get the genesis time and genesis delay
MIN_GENESIS_TIME=`curl -s $BN1_HTTP_ADDRESS/eth/v1/config/spec | jq '.data.MIN_GENESIS_TIME|tonumber'`
GENESIS_DELAY=`curl -s $BN1_HTTP_ADDRESS/eth/v1/config/spec | jq '.data.GENESIS_DELAY|tonumber'`

CURRENT_TIME=`date +%s`
# Note: doppelganger protection can only be started post epoch 0
echo "Waiting until next epoch before starting the next validator client..."
DELAY=$((($SLOT_DURATION_MS / 1000) * 32 + $GENESIS_DELAY + $MIN_GENESIS_TIME - $CURRENT_TIME))
sleep $DELAY

# Use BN2 for the next validator client
bn_2_url=$(kurtosis service inspect $ENCLAVE_NAME cl-2-lighthouse-geth | grep -oP '(?<=--enr-address=)[^ ]+')
bn_2_port=4000

if [[ "$BEHAVIOR" == "failure" ]]; then

    echo "Starting the doppelganger validator client."

    # Use same keys as keys from VC1 and connect to BN2
    # This process should not last longer than 2 epochs
    vc_1_range_start=0
    vc_1_range_end=$(($KEYS_PER_NODE - 1))
    vc_1_keys_artifact_id="1-lighthouse-geth-$vc_1_range_start-$vc_1_range_end"
    service_name=vc-1-doppelganger

    kurtosis service add $ENCLAVE_NAME $service_name --json-service-config - << EOF
    {
        "image": "$LH_IMAGE_NAME",
        "files": {
            "/validator_keys": ["$vc_1_keys_artifact_id"],
            "/testnet": ["el_cl_genesis_data"]
        },
        "cmd": [
            "lighthouse",
            "vc",
            "--debug-level", "info",
            "--testnet-dir=/testnet",
            "--validators-dir=/validator_keys/keys",
            "--secrets-dir=/validator_keys/secrets",
            "--init-slashing-protection",
            "--beacon-nodes=http://$bn_2_url:$bn_2_port",
            "--enable-doppelganger-protection",
            "--suggested-fee-recipient", "0x690B9A9E9aa1C9dB991C7721a92d351Db4FaC990"
        ]
    }
EOF

    # Check if doppelganger VC has stopped and exited. Exit code 1 means the check timed out and VC is still running.
    check_exit_cmd="until [ \$(get_service_status $service_name) != 'RUNNING' ]; do sleep 1; done"
    doppelganger_exit=$(run_command_without_exit "timeout $((($SLOT_DURATION_MS / 1000) * 32 * 2 )) bash -c \"$check_exit_cmd\"")

    if [[ $doppelganger_exit -eq 1 ]]; then
        echo "Test failed: expected doppelganger but VC is still running. Check the logs for details."
        exit_and_dump_logs 1
    else
        echo "Test passed: doppelganger found and VC process stopped successfully."
        exit_and_dump_logs 0
    fi

fi

if [[ "$BEHAVIOR" == "success" ]]; then

    echo "Starting the last validator client."

    vc_4_range_start=$(($KEYS_PER_NODE * 3))
    vc_4_range_end=$(($KEYS_PER_NODE * 4 - 1))
    vc_4_keys_artifact_id="4-lighthouse-geth-$vc_4_range_start-$vc_4_range_end"
    service_name=vc-4

    kurtosis service add $ENCLAVE_NAME $service_name --json-service-config - << EOF
    {
        "image": "$LH_IMAGE_NAME",
        "files": {
            "/validator_keys": ["$vc_4_keys_artifact_id"],
            "/testnet": ["el_cl_genesis_data"]
        },
        "cmd": [
            "lighthouse",
            "vc",
            "--debug-level", "info",
            "--testnet-dir=/testnet",
            "--validators-dir=/validator_keys/keys",
            "--secrets-dir=/validator_keys/secrets",
            "--init-slashing-protection",
            "--beacon-nodes=http://$bn_2_url:$bn_2_port",
            "--enable-doppelganger-protection",
            "--suggested-fee-recipient", "0x690B9A9E9aa1C9dB991C7721a92d351Db4FaC990"
        ]
    }
EOF

    doppelganger_failure=0

    # Sleep three epochs, then make sure all validators were active in epoch 2. Use
    # `is_previous_epoch_target_attester` from epoch 3 for a complete view of epoch 2 inclusion.
    #
    # See: https://lighthouse-book.sigmaprime.io/api_validator_inclusion.html
    echo "Waiting three epochs..."
    sleep $((($SLOT_DURATION_MS / 1000) * 32 * 3 ))

    # Get VC4 validator keys
    keys_path=$SCRIPT_DIR/$ENCLAVE_NAME/node_4/validators
    rm -rf $keys_path && mkdir -p $keys_path
    kurtosis files download $ENCLAVE_NAME $vc_4_keys_artifact_id $keys_path
    cd $keys_path/keys

    for val in 0x*; do
        [[ -e $val ]] || continue
        is_attester=$(run_command_without_exit "curl -s $BN1_HTTP_ADDRESS/lighthouse/validator_inclusion/3/$val | jq | grep -q '\"is_previous_epoch_target_attester\": false'")
        if [[ $is_attester -eq 0 ]]; then
            echo "$val did not attest in epoch 2."
        else
            echo "ERROR! $val did attest in epoch 2."
            doppelganger_failure=1
        fi
    done

    if [[ $doppelganger_failure -eq 1 ]]; then
        exit_and_dump_logs 1
    fi

    # Sleep two epochs, then make sure all validators were active in epoch 4. Use
    # `is_previous_epoch_target_attester` from epoch 5 for a complete view of epoch 4 inclusion.
    #
    # See: https://lighthouse-book.sigmaprime.io/api_validator_inclusion.html
    echo "Waiting two more epochs..."
    sleep $(( $SLOT_DURATION_MS / 1000 * 32 * 2 ))
    for val in 0x*; do
        [[ -e $val ]] || continue
        is_attester=$(run_command_without_exit "curl -s $BN1_HTTP_ADDRESS/lighthouse/validator_inclusion/5/$val | jq | grep -q '\"is_previous_epoch_target_attester\": true'")
        if [[ $is_attester -eq 0 ]]; then
            echo "$val attested in epoch 4."
        else
            echo "ERROR! $val did not attest in epoch 4."
            doppelganger_failure=1
        fi
    done

    if [[ $doppelganger_failure -eq 1 ]]; then
        exit_and_dump_logs 1
    fi
fi

exit_and_dump_logs 0
