Debug validator checkpoint inconsistencies where some validators are behind others...
Alert-based triggers:
User request triggers:
default_ism app context if not specified)| Parameter | Required | Default | Description |
|---|---|---|---|
origin_chain |
Yes | - | The origin chain where validators are signing checkpoints (e.g., hyperevm, ethereum, arbitrum) |
app_context |
No | default_ism |
The ISM/application context (e.g., default_ism, EZETH/renzo-prod) |
time_range |
No | 1h |
How far back to investigate |
environment |
No | mainnet3 |
Only possible values are mainnet3 or testnet4. If told it's mainnet or testnet, pick the appropriate possible value. |
validator |
No | * |
Optional |
evaluation_timestamp |
No | Now | A timestamp to consider as the current time. A timestamp in the genuine past may be used here to eval previous scenarios. Treat this timestamp as if it were the current time for all requests to tools, e.g. when querying Prometheus metrics. Never query any metrics after the evaluation_timestamp. |
CRITICAL: Use the relayer's perspective to see ALL validators (including external):
Use the Grafana MCP server to query prometheus metrics. Use datasource uid grafanacloud-prom to interact with Prometheus metrics.
If a specific evaluation_timestamp is provided that is not Now, be sure to always behave as if evaluation_timestamp is the present time. This means:
evaluation_timestamp at the very newest - never query anything past this timeevaluation_timestampevaluation_timestamp as the end time when querying any prometheus metrics, NOT the start time.The following metric exists that shows the relayer's perspective of all validators:
hyperlane_observed_validator_latest_index{
origin="[origin_chain]",
destination="[destination_chain]",
hyperlane_deployment="[environment]",
app_context="[APP_CONTEXT]",
validator="[validator_address]",
hyperlane_context="hyperlane" # always use this
}
The metric shows the latest highest signed index by a validator as it's observed by the relayer. It's only updated by the relayer when it is attempting to process a message from that origin to that destination for the given app_context. This has the following implications:
max by (origin, validator, app_context) on the metric if you want to see the latest values for each validator in that validator setmax by (origin, validator, app_context)NEVER ignore negative values from these metrics. A -1 value here means that the validator signatures are not accessible by the relayer. Consider this as serious as if the validator were completely down. Note that the root cause of this is one of the following:
If you have the ability to access known issues, before proposing remediation, check whether this alert pattern matches an active known issue.
Treat this as likely known when all of the following are true:
When it matches, explicitly say this appears to be a known issue and summarize:
Typical shape of a known-issue match:
If you have the ability to track known issues and the issue is not yet tracked as known but appears recurring + currently unactionable, recommend saving it as a known issue candidate.
Skip this step if an app_context was explicitly specified. Continue this step if no app_context was specified, even if it has a default value.
Skip this step if no specific validator was specified.
If any validator was specified, first use the hyperlane_observed_validator_latest_index metric to find the app_contexts to focus on. Find the values of the app_context label for the metric:
hyperlane_observed_validator_latest_index{origin="[origin_chain]", validator="[validator]"}
If the list of app contexts includes default_ism, we will set the app_context for future use as default_ism. If not, pick any one of the returned values as the app_context.
hyperlane_observed_validator_latest_index to see which validators may be behindFirst, see the highest value of each validator from the origin chain:
Look at the following metric values for the last time_range, one data point every minute.
max_over_time(
max by (validator) (
hyperlane_observed_validator_latest_index{hyperlane_context="hyperlane", hyperlane_deployment="[environment]", origin="[origin_chain]", app_context=~"[app_context]"}
)[3h:]
)
A stalled validator will both:
Note if a validator satisfies the above points to be a stalled validator, it is considered stalled even if it is just a few indices behind. DO NOT consider it "slightly behind" even if it's just 5 indices behind -- if it is stalled, it's stalled.
If there are no stalled validators, then skip to Step 2.
Then, confirm that the validators are genuinely behind
If a validator seems stalled based off the metrics we just got, the next step is to ensure that the validator is enrolled for all destination chains, and that we aren't hitting a situation where the validator is only enrolled for some destination chains with infrequent messages, so it just hasn't been updated in a while.
Note it's totally possible for a validator to be genuinely stalled and to also only be enrolled in a subset of destination chains. We care just as much about these validators that are stalled even if they are still enrolled when they shouldn't be.
For each potentially stalled validator:
destination label values for the hyperlane_observed_validator_latest_index{origin="[origin_chain]", validator="[stalled_validator]}hyperlane_observed_validator_latest_index{origin="[origin_chain]"}. If there is no difference in the destination chains, this validator is genuinely stalled.destination=~"[list of destination chains the stalled validator has, in promql compatible format]". If no validators seem behind once we filter for just the destination chains that the stalled validator is enrolled on, then nothing is actually stalled!Perform this for all validators on the origin chain, regardless of them being stalled or not:
For each validator address:
typescript/sdk/src/consts/multisigIsm.tsdefaultMultisigConfigs. This is the latest source of truth for the validator set relating to the origin chain.Additionally, take note of the threshold and validator set size of the origin chain.
Always surface the ENTIRE validator address (NEVER truncate), alias, latest signed checkpoint, difference from the highest latest signed checkpoint, status (e.g. healthy, stalled).
First, show all validators that are present in the multisigIsm.ts configuration in a table. Clearly show any validators here that are stalled, and the other non-stalled validators as healthy.
Second, show in a table any observed validators that are not present in the multisigIsm.ts file but are still seemingly enrolled for some destinations. Surface these as a warning, and include the list of destination chains they are still enrolled on. Clearly show any validators here that are stalled, and the other non-stalled validators as healthy. Treat any difference here from the default ISM as a possible erroneous configuration, even if you think it may be the case that a custom ISM is intentionally be used.
Then summarize the key findings:
threshold validators healthy. If the # of stalled validators means that we no longer meet the threshold of healthy validators, this is extremely concerning and is high priority! If we are close (i.e. if one more goes down), treat this as medium priority. Otherwise, treat this as low priority unless instructions below conflict with this.