Living in a state of accord.

Creating validator password files

Teku requires a password file for each individual keystore, with the same relative path as the keystore but a .txtextension.  If all the keystores have the same password, you can copy a single password.txtfile for all the keystores with the command below:

for keyFile in keys/*; do cp password.txt "secrets/$(echo "$keyFile" | sed -e 's/keys\/\(.*\)\.json/\1/').txt"; done

Note that the keys directory is specified twice in that command so if the keys are in a different directory name, make sure to update both places.

Easy SSH Access to EC2 Hosts

We run a bunch of hosts on EC2 and while most just have the default DNS name, they all have a Name tag to identify their purpose. While there’s lots of automation setup via ansible, it is often still necessary to SSH directly into a particular box, especially while debugging issues.

I find it annoying to have to go log into the AWS console just to find the DNS name of the particular box I want so I’ve written a little script that searches for hosts based on the name tag and can then SSH into them.

It requires having the aws  utility setup and able to login, plus having jq installed.

set -euo pipefail
RESPONSE=`aws ec2 describe-instances`
INSTANCES=`jq "[.Reservations[].Instances[] 
    | select(.State.Code == 16) 
    | select(.PublicDnsName != \"\") 
    | select(((.Tags // [])[] | select(.Key == \"Name\")).Value | test(\"$HOST_NAME\"))] 
    | sort_by((.Tags[] | select(.Key == \"Name\")).Value)" <<< "$RESPONSE"`
HOSTS=(`jq -r '.[].PublicDnsName' <<< "$INSTANCES"`)
if (( $HOST_COUNT == 0 ))
 echo "No matching hosts"
 exit 1
elif (( $HOST_COUNT > 1 ))
 NAMES=(`jq -r '.[].Tags[] | select(.Key == "Name").Value' <<< "$INSTANCES"`)
 echo "Multiple matching hosts found: "
 for NAME in ${NAMES[@]}
    HOSTNAME=${HOSTS[$((NUM - 1))]}
    echo "$NUM: $NAME  ($HOSTNAME)"
    NUM=$((NUM + 1))
 read -p "Enter the number of the host you meant: " -r SELECTION
 ssh ${HOSTS[0]}

Run with awssh <name> where <name> is a regex that matches any Name tag.  Typically a substring match is simplest.  So to SSH to the Medalla testnet bootnode we run I’d just use awssh medalla-bootnode.

If more than one node matches it provides a list of the matching nodes, so to select one of the Medalla nodes I’d run awssh medalla then select from the resulting list.

Exploring Ethereum 2: The Curious Case of the Invisible Fork

It’s like something out of a Sherlock novel – the doors are all locked, windows barred and yet somehow the jewels have been stolen. Or in this case, every block created forms a single chain and yet somehow the validator client has wound up on the wrong fork. Fortunately Sherlock, or rather Cem Ozer, was on the case. This is the story behind those very annoying “Produced invalid attestation” errors that Teku would sometimes generate.

The blocks involved look a lot like:

Block Root0x01


Parent Root0x00



There are no other blocks floating around. This looks like a chain working perfectly with no forks. However, timing matters in ETH2 and there’s an invisible fork hidden in there. Slot 32 is the start of a new epoch so validator needs to calculate the duties it should perform but in this case the block for slot 32 arrived late, so the validator calculated it’s duties based on:

Block Root0x01


Parent Root0x00



While in ETH1 that would just mean you’re behind head, in ETH2 whether a block exists or not effectively creates a fork. That missing block contributes to the randao value and so all the committee shuffling and duty scheduling based off the randao changes depending on whether the slot is empty or not.

So when the validator calculates its duties based on slot 32 being empty, it gets a different set of duties than it would if the block had already arrived. The net result is that attestation signatures appear invalid because the validator they come from is calculated based on the shuffling, not explicitly stated in the attestation.

Later when the block for slot 32 does arrive, the beacon chain considers it as just an extension of the current fork, so doesn’t tell the validator client to recalculate duties. An epoch later when those scheduled duties actually happen, they’re still scheduled as if that slot was empty and so the signatures appear invalid.

Cem’s fix is elementary (as most things are once you understand the problem) – the beacon chain node needs to fire a reorg event when a previously empty slot is filled, which causes the validator client to recalculate its duties.

So case closed? Not quite… We’d actually already thought of this potential problem and the validator client was already listening for block imported events. When a block was imported, any duties from two or more epochs were invalidated (at the start of an epoch, you can safely calculate duties for that epoch and the one after, so the blocks in epoch 3 only affects duties for epoch 5 and later). That should have caught this case – why was the problem still happening?

It turns out that while the duties were correctly invalidated when the block was imported, block import isn’t what updates the best block – running the fork choice algorithm does. It turns out the validator client wound up recalculating the duties before fork choice had been run to consider the new block and thus recalculated duties based on the slot still being empty. With Cem’s fix in place we’ll be able to remove this first attempt at a fix and only invalidate on re-org events.


Stumbled across ArchUnit today which looks useful. In particular I think there’s power in being able to assert that certain packages really never depend on each other. Although gradle/maven modules would probably be a better level to assert at.  It’s depressingly common for code bases to be split into separate modules with the intention that they be a clear separation of concerns only for a web of dependencies to be added because someone wanted to reuse some class and didn’t refactor to a common module.

Of course, it may not be that difficult to write module level tests in Gradle – the dependency structure is already there and easy to work with…