Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 46 additions & 16 deletions modules/dora/examples.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,42 @@ dora_network_health:
networks = dora.list_networks()
print(f"Available networks: {[n['name'] for n in networks]}")

# Get network overview for a specific network
# Get network overview for a specific network.
# Keys: current_epoch, current_slot, finalized (bool), participation_rate (%).
network = "sepolia"
overview = dora.get_network_overview(network)
print(f"Current epoch: {overview['current_epoch']}")
print(f"Current slot: {overview['current_slot']}")
print(f"Active validators: {overview['active_validator_count']}")
print(f"Participation: {overview['participation_rate']}% (>66.7% needed to finalize)")

- name: Check network finality
description: Check if the network is finalizing properly
query: |
from ethpandaops import dora

overview = dora.get_network_overview("sepolia")
finalized_epoch = overview.get("finalized_epoch", 0)
current_epoch = overview.get("current_epoch", 0)
# get_network_overview's `finalized` is a bool, not an epoch. To measure the
# finality lag, scan recent epochs (get_epoch has `epoch` + `finalized`) and
# find the most recent finalized one.
network = "sepolia"
Comment thread
parithosh marked this conversation as resolved.
overview = dora.get_network_overview(network)
current_epoch = overview["current_epoch"]

epochs_behind = current_epoch - finalized_epoch
if epochs_behind <= 2:
print(f"Network is finalizing normally (finalized epoch: {finalized_epoch})")
finalized_epoch = None
for e in range(current_epoch - 2, current_epoch - 12, -1):
try:
if dora.get_epoch(network, e).get("finalized"):
finalized_epoch = e
break
except Exception:
continue

if finalized_epoch is None:
print(f"No finalized epoch in the last 10 — finality may be stalled (head epoch {current_epoch})")
else:
print(f"Warning: Network is {epochs_behind} epochs behind finality")
behind = current_epoch - finalized_epoch
note = "normal" if behind <= 3 else "LAGGING"
print(f"Finalized epoch {finalized_epoch}, {behind} behind head ({note})")
print(f"Participation: {overview['participation_rate']}% (>66.7% needed to finalize)")

- name: Detect network splits
description: Check if the network has split into multiple forks, detect consensus splits and fork detection
Expand All @@ -42,8 +57,13 @@ dora_network_health:
network = "sepolia"
base_url = dora.get_base_url(network)

with httpx.Client(timeout=30) as client:
resp = client.get(f"{base_url}/forks", headers={"Accept": "application/json"})
# Hosted Dora sits behind Cloudflare — send a browser User-Agent or it 403s (Error 1010).
headers = {
"Accept": "application/json",
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36",
}
with httpx.Client(timeout=30, headers=headers) as client:
resp = client.get(f"{base_url}/forks")
resp.raise_for_status()
forks = resp.json()

Expand All @@ -69,7 +89,7 @@ dora_validator_queries:
print(f"Validator 12345:")
print(f" Status: {validator['status']}")
print(f" Balance: {validator['balance']} gwei")
print(f" Activation epoch: {validator.get('activation_epoch', 'N/A')}")
print(f" Activation epoch: {validator.get('activationepoch', 'N/A')}")

# Generate a link to view in Dora
link = dora.link_validator("sepolia", "12345")
Expand All @@ -95,8 +115,13 @@ dora_validator_queries:
network = "sepolia"
base_url = dora.get_base_url(network)

# Hosted Dora sits behind Cloudflare — send a browser User-Agent or it 403s (Error 1010).
headers = {
"Accept": "application/json",
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36",
}
# group=3 groups by node name, order=offline-d sorts by offline count descending
with httpx.Client(timeout=30) as client:
with httpx.Client(timeout=30, headers=headers) as client:
resp = client.get(f"{base_url}/api/v1/validators/activity", params={
"group": 3,
"order": "offline-d",
Expand Down Expand Up @@ -142,7 +167,7 @@ dora_slot_epoch_queries:
# Get epoch details (previous epoch for complete data)
epoch_info = dora.get_epoch("sepolia", current_epoch - 1)
print(f"Epoch {current_epoch - 1}:")
print(f" Participation rate: {epoch_info.get('validator_participation', 'N/A')}")
print(f" Participation rate: {epoch_info.get('globalparticipationrate', 'N/A')}")
print(f" Finalized: {epoch_info.get('finalized', False)}")

print(f" View in Dora: {dora.link_epoch('sepolia', current_epoch - 1)}")
Expand All @@ -158,9 +183,14 @@ dora_slot_epoch_queries:
overview = dora.get_network_overview(network)
current_slot = overview["current_slot"]

# Hosted Dora sits behind Cloudflare — send a browser User-Agent or it 403s (Error 1010).
headers = {
"Accept": "application/json",
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36",
}
# with_missing=2 returns only missing/orphaned slots
slot_lookback = 300 # ~1 hour
with httpx.Client(timeout=30) as client:
with httpx.Client(timeout=30, headers=headers) as client:
resp = client.get(f"{base_url}/api/v1/slots", params={
"with_missing": 2,
"min_slot": current_slot - slot_lookback,
Expand Down Expand Up @@ -213,7 +243,7 @@ dora_combined_workflows:
print(f"=== {network.upper()} Network Status ===")
print(f"Current slot: {overview['current_slot']}")
print(f"Current epoch: {overview['current_epoch']}")
print(f"Active validators: {overview['active_validator_count']}")
print(f"Participation: {overview['participation_rate']}%")

# Get recent blocks from Xatu for deeper analysis
df = clickhouse.query("clickhouse-raw", f'''
Expand Down
40 changes: 20 additions & 20 deletions modules/ethnode/examples.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ ethnode_sync_status:
query: |
from ethpandaops import ethnode

network = "dencun-devnet-12"
instance = "lighthouse-geth-1"
network = "<network>" # discover with ethnode.list_networks()
instance = "<instance>" # a node host.name on that network (e.g. lighthouse-geth-1)

syncing = ethnode.get_node_syncing(network, instance)
print(f"Node {instance} sync status:")
Expand All @@ -21,8 +21,8 @@ ethnode_sync_status:
query: |
from ethpandaops import ethnode

network = "dencun-devnet-12"
instance = "lighthouse-geth-1"
network = "<network>" # discover with ethnode.list_networks()
instance = "<instance>" # a node host.name on that network (e.g. lighthouse-geth-1)

syncing = ethnode.eth_syncing(network, instance)
block_number = ethnode.eth_block_number(network, instance)
Expand All @@ -34,8 +34,8 @@ ethnode_sync_status:
query: |
from ethpandaops import ethnode

network = "dencun-devnet-12"
instances = ["lighthouse-geth-1", "prysm-geth-1", "teku-geth-1"]
network = "<network>" # discover with ethnode.list_networks()
instances = ["<instance-1>", "<instance-2>", "<instance-3>"] # node host.names on that network

for instance in instances:
try:
Expand All @@ -54,8 +54,8 @@ ethnode_node_info:
query: |
from ethpandaops import ethnode

network = "dencun-devnet-12"
instance = "lighthouse-geth-1"
network = "<network>" # discover with ethnode.list_networks()
instance = "<instance>" # a node host.name on that network (e.g. lighthouse-geth-1)

# Beacon node version
version = ethnode.get_node_version(network, instance)
Expand All @@ -70,8 +70,8 @@ ethnode_node_info:
query: |
from ethpandaops import ethnode

network = "dencun-devnet-12"
instance = "lighthouse-geth-1"
network = "<network>" # discover with ethnode.list_networks()
instance = "<instance>" # a node host.name on that network (e.g. lighthouse-geth-1)

# CL peers
peer_count = ethnode.get_peer_count(network, instance)
Expand All @@ -90,8 +90,8 @@ ethnode_chain_status:
query: |
from ethpandaops import ethnode

network = "dencun-devnet-12"
instance = "lighthouse-geth-1"
network = "<network>" # discover with ethnode.list_networks()
instance = "<instance>" # a node host.name on that network (e.g. lighthouse-geth-1)

checkpoints = ethnode.get_finality_checkpoints(network, instance)
data = checkpoints['data']
Expand All @@ -104,8 +104,8 @@ ethnode_chain_status:
query: |
from ethpandaops import ethnode

network = "dencun-devnet-12"
instance = "lighthouse-geth-1"
network = "<network>" # discover with ethnode.list_networks()
instance = "<instance>" # a node host.name on that network (e.g. lighthouse-geth-1)

spec = ethnode.get_config_spec(network, instance)
print(f"Config name: {spec['data'].get('CONFIG_NAME', 'unknown')}")
Expand All @@ -120,8 +120,8 @@ ethnode_chain_status:
query: |
from ethpandaops import ethnode

network = "dencun-devnet-12"
instance = "lighthouse-geth-1"
network = "<network>" # discover with ethnode.list_networks()
instance = "<instance>" # a node host.name on that network (e.g. lighthouse-geth-1)

# Latest beacon header
header = ethnode.get_beacon_headers(network, instance)
Expand All @@ -141,8 +141,8 @@ ethnode_advanced:
query: |
from ethpandaops import ethnode

network = "dencun-devnet-12"
instance = "lighthouse-geth-1"
network = "<network>" # discover with ethnode.list_networks()
instance = "<instance>" # a node host.name on that network (e.g. lighthouse-geth-1)

# Get deposit contract info
deposit = ethnode.beacon_get(network, instance, "/eth/v1/config/deposit_contract")
Expand All @@ -157,8 +157,8 @@ ethnode_advanced:
query: |
from ethpandaops import ethnode

network = "dencun-devnet-12"
instance = "lighthouse-geth-1"
network = "<network>" # discover with ethnode.list_networks()
instance = "<instance>" # a node host.name on that network (e.g. lighthouse-geth-1)

# Get a specific block
block = ethnode.execution_rpc(network, instance, "eth_getBlockByNumber", ["latest", False])
Expand Down
Loading