Skip to content

Instantly share code, notes, and snippets.

@samcm
Last active February 27, 2025 23:33
Show Gist options
  • Save samcm/e2da294dab77e93ad0ee0e815580294f to your computer and use it in GitHub Desktop.
Save samcm/e2da294dab77e93ad0ee0e815580294f to your computer and use it in GitHub Desktop.

check-holesky.sh

check-holesky.sh is a script that verifies if a beacon node is on the correct fork by comparing its block roots with a reference chain data file. It has been preconfigured with the slot -> block root mappings generated from a lighthouse-erigon node.

Usage

wget https://gist.githubusercontent.com/samcm/e2da294dab77e93ad0ee0e815580294f/raw/b379bd5417409d2b01c7c3dd9d3348c91e80b348/check-holesky.sh \
> check-holesky.sh

chmod +x check-holeksy.sh

# Custom values
BEACON_NODE_URL="http://localhost:5052" ./check-holesky.sh

Environment Variables

Variable Description Default
CHAIN_DATA_URL URL to the reference chain data JSON file https://gist.githubusercontent.com/samcm/e2da294dab77e93ad0ee0e815580294f/raw/d9f71a035185f10d2a7fff887f0a5823b430bf71/holesky-lighthouse-erigon.json
BEACON_NODE_URL URL to the beacon node API http://localhost:5052

Requirements

  • bash
  • curl
  • jq
  • Access to a beacon node API endpoint

Expected output

Good chain

🔍 Checking slots starting from 3712450 (epoch 116014)...
⏭️  Slot 3712450 (epoch 116014) not found on node, trying earlier slot...
✅ Matching root at slot 3712443 (epoch 116013): 0xc680de17afc7d73cda381bb1ec743e63d6123fd255ebb6e682cf1c59702afcb4
✅ Node is on the correct fork

Bad chain

⏭️  Slot 3712443 (epoch 116013) not found on node, trying earlier slot...
⏭️  Slot 3712415 (epoch 116012) not found on node, trying earlier slot...
⏭️  Slot 3712355 (epoch 116011) not found on node, trying earlier slot...
⏭️  Slot 3712292 (epoch 116009) not found on node, trying earlier slot...
⏭️  Slot 3711917 (epoch 115997) not found on node, trying earlier slot...
❌ Fork detected at slot 3711913 (epoch 115997)!
  Expected root: 0xcd2f0403e62139d1e77440d66df9abacc2c706e45527e95cc51569c9d328de9a
  Node's root:   0x03e705ddb8526f702992e005ae27c9dd10dff5a4cf070cc7a3142c02c487e123
🔍 Continuing to search for fork point...
⏭️  Slot 3711901 (epoch 115996) not found on node, trying earlier slot...
⏭️  Slot 3711860 (epoch 115995) not found on node, trying earlier slot...
⏭️  Slot 3711858 (epoch 115995) not found on node, trying earlier slot...
⏭️  Slot 3711831 (epoch 115994) not found on node, trying earlier slot...
⏭️  Slot 3711791 (epoch 115993) not found on node, trying earlier slot...
⏭️  Slot 3711703 (epoch 115990) not found on node, trying earlier slot...
⏭️  Slot 3711699 (epoch 115990) not found on node, trying earlier slot...
⏭️  Slot 3711682 (epoch 115990) not found on node, trying earlier slot...
⏭️  Slot 3711672 (epoch 115989) not found on node, trying earlier slot...
⏭️  Slot 3711668 (epoch 115989) not found on node, trying earlier slot...
⏭️  Slot 3711649 (epoch 115989) not found on node, trying earlier slot...
⏭️  Slot 3711641 (epoch 115988) not found on node, trying earlier slot...
⏭️  Slot 3711636 (epoch 115988) not found on node, trying earlier slot...
⏭️  Slot 3711574 (epoch 115986) not found on node, trying earlier slot...
⏭️  Slot 3711477 (epoch 115983) not found on node, trying earlier slot...
⏭️  Slot 3711461 (epoch 115983) not found on node, trying earlier slot...
⏭️  Slot 3711433 (epoch 115982) not found on node, trying earlier slot...
⏭️  Slot 3711416 (epoch 115981) not found on node, trying earlier slot...
⏭️  Slot 3711391 (epoch 115980) not found on node, trying earlier slot...
⏭️  Slot 3711388 (epoch 115980) not found on node, trying earlier slot...
⏭️  Slot 3711311 (epoch 115978) not found on node, trying earlier slot...
⏭️  Slot 3711290 (epoch 115977) not found on node, trying earlier slot...
⏭️  Slot 3711171 (epoch 115974) not found on node, trying earlier slot...
⏭️  Slot 3711036 (epoch 115969) not found on node, trying earlier slot...
❌ Node is on a different fork, and no common ancestor was found
#!/bin/bash
# Usage: CHAIN_DATA_URL=https://gist.githubusercontent.com/samcm/e2da294dab77e93ad0ee0e815580294f/raw/da79a6bd82f6d07e8caca11beb27bdab8b29a7d3/holesky-lighthouse-erigon.json BEACON_NODE_URL=http://localhost:5052 ./check.sh
# Default CHAIN_DATA_URL if not provided
CHAIN_DATA_URL=${CHAIN_DATA_URL:-"https://gist.githubusercontent.com/samcm/e2da294dab77e93ad0ee0e815580294f/raw/da79a6bd82f6d07e8caca11beb27bdab8b29a7d3/holesky-lighthouse-erigon.json"}
# Default BEACON_NODE_URL if not provided
BEACON_NODE_URL=${BEACON_NODE_URL:-"http://localhost:5052"}
echo "CHAIN_DATA_URL: $CHAIN_DATA_URL"
echo "BEACON_NODE_URL: $BEACON_NODE_URL"
# Download chain data
CHAIN_DATA=$(curl -s "$CHAIN_DATA_URL")
if [ $? -ne 0 ]; then
echo "❌ Failed to download chain data from $CHAIN_DATA_URL"
exit 1
fi
# Get highest slot from chain data
HIGHEST_SLOT=$(echo "$CHAIN_DATA" | jq -r 'keys | map(tonumber) | max')
if [ -z "$HIGHEST_SLOT" ] || [ "$HIGHEST_SLOT" = "null" ]; then
echo "❌ No slots found in chain data"
exit 1
fi
# Calculate highest epoch
HIGHEST_EPOCH=$((HIGHEST_SLOT / 32))
echo "🔍 Checking slots starting from $HIGHEST_SLOT (epoch $HIGHEST_EPOCH)..."
# Get all slots in descending order
SLOTS=$(echo "$CHAIN_DATA" | jq -r 'keys | map(tonumber) | sort | reverse | .[]')
# Track if we found a match
FOUND_MATCH=false
# Track if we found a fork
FOUND_FORK=false
# Track the fork point
FORK_SLOT=""
FORK_EPOCH=""
FORK_EXPECTED_ROOT=""
FORK_NODE_ROOT=""
for SLOT in $SLOTS; do
# Calculate epoch for this slot
CURRENT_EPOCH=$((SLOT / 32))
# Get expected root for this slot
EXPECTED_ROOT=$(echo "$CHAIN_DATA" | jq -r --arg SLOT "$SLOT" '.[$SLOT]')
# Try to get the block at this slot from the beacon node
NODE_DATA=$(curl -s "$BEACON_NODE_URL/eth/v1/beacon/headers/$SLOT")
STATUS=$?
# Check if request was successful
if [ $STATUS -eq 0 ] && [ "$(echo "$NODE_DATA" | jq 'has("data")')" = "true" ]; then
# Extract root from node
NODE_ROOT=$(echo "$NODE_DATA" | jq -r '.data.root')
# Compare roots
if [ "$NODE_ROOT" = "$EXPECTED_ROOT" ]; then
echo "✅ Matching root at slot $SLOT (epoch $CURRENT_EPOCH): $NODE_ROOT"
FOUND_MATCH=true
# If we previously found a fork, this is where the chains converge again
if [ "$FOUND_FORK" = true ]; then
echo "🔀 Chains converge at slot $SLOT (epoch $CURRENT_EPOCH)"
break
else
# If we haven't found a fork and found a match, we're on the right chain
break
fi
else
# We found a fork
if [ "$FOUND_FORK" = false ]; then
# This is the first fork point we found
FOUND_FORK=true
FORK_SLOT=$SLOT
FORK_EPOCH=$CURRENT_EPOCH
FORK_EXPECTED_ROOT=$EXPECTED_ROOT
FORK_NODE_ROOT=$NODE_ROOT
echo "❌ Fork detected at slot $SLOT (epoch $CURRENT_EPOCH)!"
echo " Expected root: $EXPECTED_ROOT"
echo " Node's root: $NODE_ROOT"
echo "🔍 Continuing to search for fork point..."
fi
fi
else
echo "⏭️ Slot $SLOT (epoch $CURRENT_EPOCH) not found on node, trying earlier slot..."
fi
done
# Final status report
if [ "$FOUND_MATCH" = true ]; then
if [ "$FOUND_FORK" = true ]; then
echo "🔀 Node is on a different fork that diverged at slot $FORK_SLOT (epoch $FORK_EPOCH)"
echo " Expected root: $FORK_EXPECTED_ROOT"
echo " Node's root: $FORK_NODE_ROOT"
exit 1
else
echo "✅ Node is on the correct fork"
exit 0
fi
elif [ "$FOUND_FORK" = true ]; then
echo "❌ Node is on a different fork, and no common ancestor was found"
echo " Fork detected at slot $FORK_SLOT (epoch $FORK_EPOCH)"
exit 1
else
echo "❌ None of the slots in chain data were found on the node"
exit 1
fi
{
"3711036": "0x8ce1c41b23acbfa2748fac0725157785ae53c7200d959b22172ce8ae355a4540",
"3711058": "0x2d58d27886354a292f7247215a84b1ed0a1a714465eca800df8dd2255275eb16",
"3711098": "0xf1c7bba5b0507cf18dccfbc2d1f2c29e4c86798a95cffc190fb28968d8248eb8",
"3711149": "0x5e0e1f3b65afc3dff55d5c59593519f2b900a5e84ed7294d96992168ecc3c0d7",
"3711158": "0x9b332abea859ea938f6072ea36cd2e2bc14145cb91b43b90d75b2e8e65cc69b3",
"3711171": "0xd1f31203e6a17ffcd914ff87e5c71edc74f8a9073c2457aed3c441e08b634129",
"3711187": "0xa879c8eddbb705152c8b59a4635a193638d902aa6b5a5a5040eadf5552fd79ec",
"3711211": "0x10609ba31c3a464d2d4ab19a771bb0fa338225d2ae525f6a1ef89a4c28921638",
"3711212": "0x6d5f48dab4d4013f2aac6614a8e07206c2af3df3a8bbd2458770a230d1946427",
"3711226": "0x62f3524af504d61d46c41efb91c7cff9b02f1e1e1ad6a8b3e240651be4455f2a",
"3711241": "0xe9246c2b824b4bf8e45faccddf09bd0856941a4a30e7e6edfe30c75375c29374",
"3711248": "0x60df4819748b8d29ae027036eac2a5fd3ac9f7a7934989c3c976f7f35c46de98",
"3711290": "0xb614cfb81b86293b8810e2d1d989ba1027f3c017261ba3d86aab1bdf44004d75",
"3711291": "0x11a8c817f76b45f8cc1ae198a693dd5eaf23d83e42fb5c47b31cc080ac14df3f",
"3711304": "0xcee4240851bb75d300d59208816c99edb65b48a69fa2940462db2c5f4005249b",
"3711311": "0xc4bda7482125bb44eda96de21aae1763d99e2ac880133951578255372eb81ab8",
"3711383": "0x88b472bf721d0b9e790b56cbe4469a966a19be6626d4436a14d47090889c7213",
"3711388": "0x3b2f8f4e0af3d70622e2dff29aab5336f719b93e1017f4158ee9f17be2ff04d3",
"3711391": "0x4aa300ed631f9b5dc6a3868701f16b37c3b0b6540a402f00fb0e053cce5cba3d",
"3711393": "0x775bd90027354f81cc909f0176ec1ddeaae4cbbb0270578f1e15adeae03c144c",
"3711416": "0x439919b227023f7ee74d881231beb1b2987b180df803a242296daafac329cc13",
"3711433": "0x7d2b0ea8fcd153e8155ccde0305c542fa803c513874d2235988627cb1da1b2e6",
"3711459": "0x1ac0b5e58b7f7224fb42682d93c4a1c7b6514800d68e8ce6a4fe1392250c4531",
"3711461": "0x945bd57d63c1755d184b9da384234945f71a016304d3e3840f82636e9ead0ac1",
"3711477": "0x23f07859794b2ab81e1a08bd306b8200eac633f132be257afd5242b11a3ac38d",
"3711546": "0x8e2f3cd8d92f201c90aeeafdab51d90ff113e36fb25da5e4c8f43e87ae260083",
"3711574": "0x49968b67166ecef8a67d3c541aa230d64b565c838b32ac6d24f53a3deb1f8630",
"3711601": "0x2a354fbc24e3185189b0d8575cb295a5be96be4ccaa94f5c7aa0b9b419d8e7d9",
"3711636": "0xab9950658fe929afd005ea8c18dadad9d50013fdc773421c515cbb296760cd98",
"3711641": "0xf8abcd647e0e11ae504da33fa9828caf8e4b18e5d166295367fad32f776cb52c",
"3711649": "0x90478200789a64f1543d1a208ce03a9b59c10ea8d55655288d7d198215bcca94",
"3711668": "0xd11c01ecad31e998ab66cfce44aaa082adb78394d8479008cbcb702efeb3a732",
"3711672": "0x64c6098e002eb445c1328f720e6fa1e2fc167d5f77c7740c5813c9d9987c5b13",
"3711682": "0x7feacdeeafeb42c844c9164162c0cbf6271120f1e9d175751302ad502a642ac2",
"3711699": "0xfeb2e078a89a480aecd8e631501896ff2a7d45dbf87021372fc98c9e64ceb90a",
"3711703": "0x6729c51dc2cecf8fb37e61fee9f1b22e5ca3a94b8b1d43f763c03e8b8aae47c9",
"3711791": "0xbcdbfa529027136454b809b7192ba966d265e7adf75ea4cea8a546d1eaaba30c",
"3711831": "0x7bf0cbdf8ac05234c23a255e710d66a4a3ca207d7097cf9a92979dcbcac8b98b",
"3711836": "0x40c640e7c24348b84741639142b50fd65c2f18e7da9c932517a74f00da16fe00",
"3711858": "0xb6b21a75a1587c3af9037946a564a58548edeac52d028b0abdd7ec9b0baeed04",
"3711860": "0x7b0aeb3fd90bd78e2f70c38f64fc7d94abeda215581793c3d111fdac325ab26a",
"3711901": "0xe372ed53b4badda9eb1f8db5549649a77886c88e91aa92f5ff3b0c934562bc1c",
"3711913": "0xcd2f0403e62139d1e77440d66df9abacc2c706e45527e95cc51569c9d328de9a",
"3711917": "0x69a749989af18cf9f77f433b4af321ee3d0f2ffaecde9c84c86cddd7fc13f2b8",
"3712292": "0xbdca18a873aec3c8d4b4461c4de229a36211175af8277d14ed287f913723712d",
"3712355": "0xae3f5b18221a717ff3691359c579a199ac4100c3f0bd59af88d50b0a6849c5e3",
"3712415": "0xc42bc8e8baa05e375faf6709ce8d98821263b7fc5c751b386edc731d17c95685",
"3712443": "0xc680de17afc7d73cda381bb1ec743e63d6123fd255ebb6e682cf1c59702afcb4",
"3712588": "0x9338ab7488652b171eed4f6c189348fcfc4575c9d6d8649a3483ecb3672974fc",
"3712679": "0xe5e0b477301367f7d55b92533595530c52f97fcf62987a334fd328fa4bc0af5c",
"3712757": "0x6382a28841f332255de45b812193f6a0ea2f6c78bf24e5badc624a023b56dbde"
}
@Lorenz29
Copy link

Typo: chmod +x check-holeksy.sh -> chmod +x check-holesky.sh

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment