name: 调查-merkle-根-不匹配 description: 调查relayer和validators之间的Merkle根不匹配警报。当警报提及“merkle根不匹配”、“检查点根与标准根不匹配”,或当要求调试某个链的relayer merkle树问题时使用。此技能仅进行调查 - 使用/fix-merkle-root-mismatch应用修复。
调查Merkle根不匹配
何时使用
-
基于警报的触发:
- 警报提及“merkle根不匹配”
- GCP日志显示:“检查点根与来自merkle证明的标准根不匹配”
hyperlane_merkle_root_mismatch指标触发
-
用户请求触发:
- “调试[链]的merkle树问题”
- “调查[链]上的merkle根不匹配”
- “为什么relayer的merkle树对于[链]是错误的?”
输入参数
| 参数 | 必需 | 默认值 | 描述 |
|---|---|---|---|
origin |
是 | - | 出现Merkle根不匹配的起源链(例如:paradex、ethereum) |
domain_id |
否 | - | 链的域ID(如未提供,则从注册表自动派生) |
environment |
否 | mainnet3 |
mainnet3 或 testnet4 |
问题概述
relayer维护一个基于消息ID构建的本地merkle树。当验证器签署检查点时,relayer需要生成merkle证明。如果relayer的树中有错误的消息ID,则根将不匹配,导致消息传递失败。
最可能的原因: Relayer索引了错误的消息ID(可能由于RPC问题或重组),而验证器拥有正确数据。
先决条件
- 对relayer pods的
kubectl访问权限 - 已配置的Grafana MCP服务器
调查工作流程
步骤1:确认警报
查询Grafana以确认不匹配指标是否触发:
使用 mcp__grafana__query_prometheus,参数:
- datasourceUid: grafanacloud-prom
- expr: hyperlane_merkle_root_mismatch{origin="[origin]"}
- startTime: now-1h
- queryType: instant
如果value为1,则确认不匹配。
步骤2:获取最新树插入索引
查询Grafana以获取当前树大小:
使用 mcp__grafana__query_prometheus,参数:
- datasourceUid: grafanacloud-prom
- expr: hyperlane_latest_tree_insertion_index{origin="[origin]", hyperlane_deployment="[environment]"}
- startTime: now-1h
- queryType: instant
这为您提供最新的叶子索引,以便向后工作。
步骤3:获取域ID
如果未提供domain_id,则从注册表获取:
curl -s "https://raw.githubusercontent.com/hyperlane-xyz/hyperlane-registry/main/chains/[origin]/metadata.yaml" | grep domainId
步骤4:建立到Relayer的端口转发
检查端口9090是否已被使用:
lsof -i :9090
如果未使用,在后台启动端口转发:
kubectl port-forward omniscient-relayer-hyperlane-agent-relayer-0 9090:9090 -n [environment] &
等待几秒钟以建立端口转发,然后验证其工作:
curl -s "localhost:9090/merkle_tree_insertions?domain_id=[domain_id]&leaf_index_start=0&leaf_index_end=1"
步骤5:二分搜索首次不匹配
比较验证器检查点(来自S3)与relayer merkle证明。使用二分搜索找到首次不匹配的索引。
验证器检查点URL模式:
https://hyperlane-[environment]-[origin]-validator-0.s3.us-east-1.amazonaws.com/checkpoint_[index]_with_id.json
比较函数:
# 检查特定索引
index=[索引]
domain_id=[域ID]
origin=[起源]
environment=[环境]
validator_root=$(curl -s "https://hyperlane-${environment}-${origin}-validator-0.s3.us-east-1.amazonaws.com/checkpoint_${index}_with_id.json" | jq -r '.value.checkpoint.root')
relayer_root=$(curl -s "localhost:9090/merkle_proofs?domain_id=${domain_id}&leaf_index=${index}&root_index=${index}" | jq -r '.root')
echo "索引 $index:"
echo " 验证器:$validator_root"
echo " Relayer:0x$relayer_root"
if [ "$validator_root" = "0x$relayer_root" ]; then echo " ✓ 匹配"; else echo " ❌ 不匹配"; fi
二分搜索策略:
- 从最新索引开始 - 如果不匹配,则跳转到该索引的50%
- 如果在50%匹配,则不匹配在50%-100%之间 - 尝试75%
- 如果在50%不匹配,则不匹配在0%-50%之间 - 尝试25%
- 继续缩小范围,直到找到确切的首次不匹配索引(索引N-1匹配但索引N不匹配)
步骤6:识别不匹配的消息ID
一旦找到首次不匹配索引,比较消息ID:
获取验证器消息ID:
for i in $(seq [首次不匹配] [首次不匹配 + 10]); do
msg=$(curl -s "https://hyperlane-[environment]-[origin]-validator-0.s3.us-east-1.amazonaws.com/checkpoint_${i}_with_id.json" | jq -r '.value.message_id')
echo "$i: $msg"
done
获取relayer消息ID:
curl -s "localhost:9090/merkle_tree_insertions?domain_id=[domain_id]&leaf_index_start=[首次不匹配]&leaf_index_end=[首次不匹配 + 10]" | jq -r '.merkle_tree_insertions[] | "\(.leaf_index): \(.message_id)"'
步骤7:获取块时间戳以获取上下文
获取首次不匹配的块号和 timestamp,以了解问题何时开始:
# 从relayer获取块号
curl -s "localhost:9090/merkle_tree_insertions?domain_id=[domain_id]&leaf_index_start=[首次不匹配]&leaf_index_end=[首次不匹配]" | jq '.merkle_tree_insertions[0].insertion_block_number'
对于EVM链,获取 timestamp:
cast block [块号] --rpc-url [rpc_url] -j | jq '.timestamp'
对于Starknet链:
curl -s --request POST --url '[rpc_url]' --header 'Content-Type: application/json' --data '{"jsonrpc":"2.0","method":"starknet_getBlockWithTxHashes","params":[{"block_number":[块号]}],"id":1}' | jq '.result.timestamp'
将Unix timestamp转换为可读格式:
date -r [timestamp] -u '+%Y-%m-%d %H:%M:%S UTC'
步骤8:报告发现
呈现调查结果:
- 摘要表:
| 参数 | 值 |
|---|---|
| 链 | [起源] |
| 域ID | [域ID] |
| 环境 | [环境] |
| 首次不匹配索引 | [索引] |
| 最新索引 | [最新] |
| 需要修复的总条目数 | [最新 - 首次不匹配 + 1] |
| 不匹配开始时间 | [块号] ([timestamp]) |
- 不匹配条目示例:
| 叶子索引 | Relayer消息ID | 验证器消息ID | 块号 |
|---|---|---|---|
| [索引] | 0x… | 0x… | [块号] |
-
通知用户:要修复此问题,他们应该运行
/fix-merkle-root-mismatch。 -
修复注意事项: 必须修复从首次不匹配到最新的所有条目,因为merkle树是累积的 - 每个根都依赖于所有先前的叶子。
API参考
Relayer端点
| 端点 | 方法 | 参数 | 描述 |
|---|---|---|---|
/merkle_tree_insertions |
GET | domain_id, leaf_index_start, leaf_index_end |
列出merkle树插入 |
/merkle_proofs |
GET | domain_id, leaf_index, root_index |
获取叶子的merkle证明 |
验证器S3检查点
https://hyperlane-[environment]-[chain]-validator-0.s3.us-east-1.amazonaws.com/checkpoint_[index]_with_id.json
响应结构:
{
"value": {
"checkpoint": {
"merkle_tree_hook_address": "0x...",
"mailbox_domain": 514051890,
"root": "0x...",
"index": 37352
},
"message_id": "0x..."
},
"signature": { ... }
}
常见问题
- 端口转发断开连接: 重新运行kubectl端口转发命令
- 验证器S3返回404: 检查点可能尚未存在于该索引处
- 二分搜索耗时过长: 初始使用更大的跳跃(例如,10000个索引)
- Shell脚本错误: 使用手动curl命令代替
rust/scripts/中的bash脚本
下一步
调查后,使用/fix-merkle-root-mismatch应用修复。
运行手册参考
完整运行手册:https://www.notion.so/hyperlanexyz/Merkle-Root-Mismatch-26a6d35200d680a2857dcd0b228d4ab7