ethereum-node icon indicating copy to clipboard operation
ethereum-node copied to clipboard

Evaluate usage of attestation/proposal/aggregation duties prediction

Open stefa2k opened this issue 3 years ago • 1 comments

Stereum users have no way of predicting duties of their validators. Please evaluate usability of such options.

Example of how prediction can work, courtesy to pk910#1214:

#!/bin/bash

# getduties.sh by pk910.eth
# This script polls validator duties and shows the next scheduled attestation/proposal.
# It's intended to be used to plan restarts of the bn / vc without missing a attestation.
# Use the `watch` command to get a live view of the output.
#
# Usage:
# ./getduties.sh [options] <validator_index> [<validator_index> ...]
#
# Options:
# -h <rpchost>  - beacon node RPC host in addr:port format (default: 127.0.0.1:5052)
# -p            - check proposer duties (disabled by default for performance)
# -v            - verbose (show more per validator details)
# -n            - always load duties of next epoch too (combine with -v)

rpchost="127.0.0.1:5052"
proposer="false"
verbose="false"
nextepoch="false"

# parse arguments
# https://stackoverflow.com/questions/192249/how-do-i-parse-command-line-arguments-in-bash
usage_error () { echo >&2 "$(basename $0):  $1"; exit 2; }
assert_argument () { test "$1" != "$EOL" || usage_error "$2 requires an argument"; }

if [ "$#" != 0 ]; then
  EOL=$(printf '\1\3\3\7')
  set -- "$@" "$EOL"
  while [ "$1" != "$EOL" ]; do
    opt="$1"; shift
    case "$opt" in

      # options
      -h|--host) assert_argument "$1" "$opt"; rpchost="$1"; shift;;
      -p|--proposer) proposer='true';;
      -v|--verbose) verbose='true';;
      -n|--nextepoch) nextepoch='true';;

      # processing
      -|''|[!-]*) set -- "$@" "$opt";;
      --*=*)      set -- "${opt%%=*}" "${opt#*=}" "$@";;
      -[!-]?*)    set -- $(echo "${opt#-}" | sed 's/\(.\)/ -\1/g') "$@";;
      --)         while [ "$1" != "$EOL" ]; do set -- "$@" "$1"; shift; done;;
      -*)         usage_error "unknown option: '$opt'";;
      *)          usage_error "this should NEVER happen ($opt)";;

    esac
  done
  shift
fi

# helper functions
function join_by {
  local d=${1-} f=${2-}
  if shift 2; then
    printf %s "$f" "${@/#/$d}"
  fi
}

function split_by {
  local delimiter=${1-}
  local str=${2-}
  local handler=$3
  local s=$str$delimiter
  while [[ $s ]]; do
    "${handler}" "${s%%"$delimiter"*}"
    s=${s#*"$delimiter"};
  done;
}

current_time=$(date +%s)
validators=$@
if [ "$validators" = "" ]; then
  echo "No validator index specified."
  exit
fi

validators_str=$(join_by , $validators)
read -ra validators_arr <<< "$validators"

echo "RPC Host: $rpchost"
echo "Validators: $validators_str"
echo "Current Time:  $current_time"


# get genesis time & calculate slot / epoch number
genesis_time=$(curl -s http://$rpchost/eth/v1/beacon/genesis | sed 's/.*"genesis_time":"\{0,1\}\([^,"]*\)"\{0,1\}.*/\1/')
slot_time=12
slot_timeout=$(expr $slot_time - \( \( $current_time - $genesis_time \) % $slot_time \) )
current_slot=$(expr \( $current_time - $genesis_time \) \/ \( $slot_time \) )
current_epoch=$(expr $current_slot \/ 32 )
current_index=$(expr $current_slot \% 32)
next_epoch=$(expr $current_epoch + 1 )
epoch_timeout=$(expr \( \( 32 - $current_index - 1 \) \* $slot_time \) + $slot_timeout)

echo "Genesis Time:  $genesis_time"
echo "Current Epoch: $current_epoch"
echo "Current Slot:  $current_slot  (idx: $current_index)"
echo "Next Epoch in: $epoch_timeout sec"
echo "Next Slot in:  $slot_timeout sec"
echo ""



# get duties
current_att=0
current_prop=0
next_att_slot=0
next_prop_slot=0

function handle_attestation_duty {
  local vidx=$(echo $1 | sed 's/.*"validator_index":"\{0,1\}\([^,"]*\)"\{0,1\}.*/\1/')
  local slot=$(echo $1 | sed 's/.*"slot":"\{0,1\}\([^,"]*\)"\{0,1\}.*/\1/')
  if [[ "$vidx" == ?(-)+([[:digit:]]) ]]; then
    if [ $verbose = "true" ]; then
      local duty_eta=$(expr \( \( $slot - $current_slot - 1 \) \* $slot_time \) + $slot_timeout )
      local eta_str=""
      if [ $duty_eta -gt 0 ]; then
        eta_str=" ETA: $duty_eta sec"
      elif [ $duty_eta -gt $(expr 0 - $slot_time) ]; then
        eta_str=" ETA: now!"
      fi
      local slot_idx=$(expr $slot \% 32)
      echo "Validator: $vidx   Attestation Slot: $slot  (idx: $slot_idx) $eta_str"
    fi
    if [ $slot -gt $current_slot ]; then
      if [ $slot -lt $next_att_slot ] || [ $next_att_slot -eq 0 ]; then
        next_att_slot=$slot
      fi
    elif [ $slot -eq $current_slot ]; then
      current_att=$vidx
    fi
  fi
}

function handle_proposer_duty {
  local vidx=$(echo $1 | sed 's/.*"validator_index":"\{0,1\}\([^,"]*\)"\{0,1\}.*/\1/')
  local slot=$(echo $1 | sed 's/.*"slot":"\{0,1\}\([^,"]*\)"\{0,1\}.*/\1/')

  if [[ "$vidx" == ?(-)+([[:digit:]]) ]] && printf '%s\0' "${validators_arr[@]}" | grep -Fxqz -- "$vidx"; then
    if [ $verbose = "true" ]; then
      local duty_eta=$(expr \( \( $slot - $current_slot - 1 \) \* $slot_time \) + $slot_timeout )
      local eta_str=""
      if [ $duty_eta -ge 0 ]; then
        eta_str=" ETA: $duty_eta sec"
      elif [ $duty_eta -gt $(expr 0 - $slot_time) ]; then
        eta_str=" ETA: now!"
      fi
      local slot_idx=$(expr $slot \% 32)
      echo "Validator: $vidx   Proposal Slot: $slot  (idx: $slot_idx) $eta_str"
    fi
    if [ $slot -gt $current_slot ]; then
      if [ $slot -lt $next_prop_slot ] || [ $next_prop_slot -eq 0 ]; then
        next_prop_slot=$slot
      fi
    elif [ $slot -eq $current_slot ]; then
      current_prop=$vidx
    fi
  fi
}

# current epoch duties
if [ $verbose = "true" ]; then
  echo "Epoch $current_epoch duties:"
fi
duties_json=$(curl -s -X POST http://$rpchost/eth/v1/validator/duties/attester/$current_epoch -H 'Content-Type: application/json' -d "[$validators_str]")
split_by "},{" "$duties_json" "handle_attestation_duty"
if [ "$proposer" = "true" ]; then
  duties_json=$(curl -s http://$rpchost/eth/v1/validator/duties/proposer/$current_epoch)
  split_by "},{" "$duties_json" "handle_proposer_duty"
fi
if [ $verbose = "true" ]; then
  echo ""
fi

# next epoch duties
if [ $nextepoch = "true" ] || [ $next_att_slot -eq 0 ]; then
  if [ $verbose = "true" ]; then
    echo "Epoch $next_epoch duties:"
  fi
  duties_json=$(curl -s -X POST http://$rpchost/eth/v1/validator/duties/attester/$next_epoch -H 'Content-Type: application/json' -d "[$validators_str]")
  split_by "},{" "$duties_json" "handle_attestation_duty"
  if [ "$proposer" = "true" ]; then
    duties_json=$(curl -s http://$rpchost/eth/v1/validator/duties/proposer/$next_epoch)
    split_by "},{" "$duties_json" "handle_proposer_duty"
  fi
  if [ $verbose = "true" ]; then
    echo ""
  fi
fi

# print summary
if [ "$current_att" -gt 0 ]; then
  echo "Attestation scheduled for current slot! (Validator: $current_att)"
fi
echo "Next Attestation Slot: $next_att_slot"
if [ "$proposer" = "true" ]; then
  if [ "$current_prop" -gt 0 ]; then
    echo "Proposal scheduled for current slot! (Validator: $current_prop)"
  fi
  echo "Next Proposal Slot: $next_prop_slot"
fi

if [ "$next_prop_slot" -gt 0 ] && [ "$next_prop_slot" -lt "$next_att_slot" ]; then
  next_duty_slot=$next_prop_slot
else
  next_duty_slot=$next_att_slot
fi

if [ $next_duty_slot -gt 0 ]; then
  remianing_slots=$(expr $next_duty_slot - $current_slot - 1)
  echo "Remaining Slots: $remianing_slots"

  remaining_time=$(expr \( $remianing_slots \* $slot_time \) + $slot_timeout )
  echo "Remaining Time: $remaining_time sec"
fi

stefa2k avatar Aug 05 '22 06:08 stefa2k

Heya, Thanks for the credits 😁

Just a few words about the script: I'm not a shell scripter and the script is definitely not production ready. The json "parsing" with regex masks hurts my eyes 😂. It was more a quick and dirty helper script for myself.

Anyway it shows how attestation & proposer duties can be fetched via the beacon node api. It would be nice to have something similar, but with proper JSON parsing and some error handling and stuff...

And it should check for sync committee contributions as well using this api: ​/eth​/v1​/validator​/duties​/sync​/{epoch}

pk910 avatar Aug 05 '22 07:08 pk910

@pk910 These has been now open for a while, but we finnaly got around adding this with the next version & we would like to give you a little image credit in there - given that you are fine that?

It will look smth like this:

Predicition Icon_variation2

And be part of the Staking page:

image

P.S.: If you have another wish, we have a great Japanese artist - who as soon as he has more time, could turn this into something more fun - if you hand us some inspiration :)

daGscheid avatar Jun 11 '23 10:06 daGscheid

Used with Rc.19 # further development tracked in #858

daGscheid avatar Jun 17 '23 15:06 daGscheid