Network Monitoring
Guide to monitoring BTX network health, difficulty tracking, and fork forensics using getdifficultyhealth and the monitor CLI.
This guide covers how to monitor BTX network health, track difficulty adjustments, detect anomalies, and investigate forks. BTX uses ASERT (aserti3-2d) per-block difficulty adjustment targeting 90-second block intervals, so continuous monitoring is essential to confirm the chain is converging correctly.
The getdifficultyhealth RPC
The primary monitoring RPC is getdifficultyhealth, which returns a comprehensive health snapshot of the network:
btx-cli getdifficultyhealth Key fields in the response:
| Field | Description |
|---|---|
target_spacing_s | Target block time (90 seconds) |
actual_spacing_mean_s | Observed mean block interval over the window |
actual_spacing_median_s | Observed median block interval |
difficulty | Current network difficulty |
asert_half_life_s | ASERT half-life (14400s before height 55000, 3600s after) |
tip_height | Current chain tip height |
tip_age_s | Age of the canonical tip in seconds |
reorg_protection | Deep-reorg protection configuration and rejection counters |
reward_concentration | Mining reward distribution across addresses |
Key Metrics to Watch
Block Intervals
The most important signal is whether actual block intervals are converging toward the 90-second target:
- Healthy: mean and median within 60-120s range
- Warning: sustained deviation beyond 2x target (180s+ mean)
- Critical:
tip_age_s > 3 * target_spacing_s(270s) while miners are active — treat as network stall
Reward Distribution
Monitor reward_concentration to detect if mining rewards are concentrating in too few addresses. High concentration may indicate a single entity controlling disproportionate hash power.
Reorg Protection
The reorg_protection section reports:
- Whether deep-reorg protection is enabled
- The maximum allowed reorg depth
- Count of
bad-reorg-depthrejections
Any non-zero rejection count warrants immediate investigation.
Peer Agreement
If a node reports We do not appear to fully agree with our peers, treat it as a first-class operator signal even if sync lag has recovered. It usually indicates peer topology or upgrade drift.
Monitor CLI Tool
The btx_difficulty_health.py tool generates comprehensive health reports across multiple nodes and time windows.
Standard 24-Hour Report
python3 btx_difficulty_health.py \
--time-window 1h=3600 \
--time-window 6h=21600 \
--time-window 24h=86400 Extended Report with 7-Day Window
python3 btx_difficulty_health.py \
--node-config /path/to/node-config.json \
--time-window 1h=3600 \
--time-window 6h=21600 \
--time-window 24h=86400 \
--time-window 7d=604800 All-Managed-Miners View
python3 btx_difficulty_health.py \
--node-config mainnet-managed-miners-node-config.json \
--time-window 1h=3600 \
--time-window 6h=21600 \
--time-window 24h=86400 Notes:
- On BTX mainnet the tool auto-sets
analysis_min_height=50000to exclude the fast-mine bootstrap phase - JSON output is the source of truth; Markdown is for operator review
- The default fleet treats only
localas an expected miner; remote archival nodes need explicit--node-configwithexpected_to_mine=true
Setting Up Alerts
Tip Staleness
Poll getdifficultyhealth on a cron interval and alert when tip_age_s exceeds a threshold:
# Alert if tip is older than 5 minutes (300s)
TIP_AGE=$(btx-cli getdifficultyhealth | jq '.tip_age_s')
if [ "$TIP_AGE" -gt 300 ]; then
echo "ALERT: Chain tip is ${TIP_AGE}s old" | notify
fi Block Interval Drift
Alert when the mean block interval deviates significantly from the 90-second target:
MEAN=$(btx-cli getdifficultyhealth | jq '.actual_spacing_mean_s')
if (( $(echo "$MEAN > 180" | bc -l) )); then
echo "ALERT: Mean block interval is ${MEAN}s (target 90s)" | notify
fi Peer Disagreement
Monitor for peer disagreement warnings across your fleet. Any node reporting disagreement should be investigated even if it appears to have recovered.
Deep Reorg Rejections
Alert on any bad-reorg-depth rejection — these indicate an attempted deep reorganization was blocked by the node's protection policy.
Fork Forensics
Use the fork forensics tool to investigate stale branches and chain splits:
python3 btx_fork_forensics.py \
--min-branchlen 2 \
--min-tip-height 50000 This reports:
- All stale tips with branch length 2 or greater
- Fork points and divergence heights
- Deep Reorg Guard section when
bad-reorg-depthrejections are detected
ASERT History Replay
Replay the ASERT difficulty adjustment history to verify correct convergence:
python3 asert_history_replay.py \
--time-window 24h=86400 \
--time-window all=-1 Diagnosing a Stalled Chain
If tip_age_s > 3 * target_spacing_s while active miners are running:
- Inspect
getchaintipson all nodes — look forheight=tip+1tips withstatus=invalid - Check
debug.logforbad-shielded-anchorentries — these indicate miners are templating transactions with stale shielded merkle anchors - Deploy the patched binary, which scrubs stale-anchor shielded entries before
CreateNewBlock() - A daemon restart clears stale entries because
mempool.datreload revalidates against the current active chain
Invalid-Tip Recovery
After deploying a validator bugfix, recover stranded nodes that cached a now-valid block as invalid:
python3 invalid_tip_recovery.py \
--node-config mainnet-managed-miners-node-config.json \
--leader local The tool reconsiders the leader's first missing block on each lagging follower and waits for automatic catch-up.
Mining Runtime Signals
The health report includes per-node mining runtime status:
| Signal | Meaning |
|---|---|
process_active=true | Miner process or loop exists |
work_signal_active=true | Node has entered the MatMul solve path and accumulated runtime counters |
in_flight_unsolved_search=true | Miner is actively scanning inside a long-running generatetoaddress call — normal for CPU miners |
Do not confuse in_flight_unsolved_search with inactivity — it simply means the miner has not yet completed a solve cycle since process start.