MSG Chain 跨链桥接与 IBC 协议指南
数据来源:MSG Chain 代码库核实
主网状态: No-Go — 当前 MSGChain 主网裁决为 No-Go,以下内容反映代码实际状态,不代表生产可用。
目录
1. 跨链架构总览
1.1 MSG Chain 跨链生态定位
MSG Chain 作为基于 Cosmos SDK 构建的 AI Agent 区块链,其跨链层目前处于 规划态。与 Cosmos 生态中成熟链的跨链状态对比如下:
| 特性 | Cosmos Hub | Osmosis | MSG Chain |
|---|---|---|---|
| IBC 状态 | 生产就绪 (v4+) | 生产就绪 (v4+) | 规划态 |
| 活跃通道数 | 100+ | 50+ | 0 (待启动) |
| 跨链清算量 | 日均数亿美元 | 日均数千万美元 | 0 |
| IBC 客户端类型 | Tendermint | Tendermint | Tendermint + Dilithium-5 (自定义) |
| 签名算法 | Ed25519/Secp256k1 | Ed25519/Secp256k1 | Dilithium-5 |
| 智能合约 | 有限 (governance) | CosmWasm | CosmWasm |
注意: MSG Chain 的跨链功能尚未在主网上线,本文档所有标记
X-MSG-Stub=true的内容均为规划中的功能。
1.2 跨链架构总览图
X-MSG-Stub=true
┌─────────────────────────────────────────────────────────────────────┐
│ MSG Chain 跨链架构 │
│ │
│ ┌─────────────────────────┐ ┌───────────────────────────────┐ │
│ │ IBC v2 协议层 │ │ 自定义桥接层 │ │
│ │ │ │ │ │
│ │ ┌─────────────────┐ │ │ ┌───────────────────┐ │ │
│ │ │ Dilithium-5 LC │ │ │ │ Lock-Mint Bridge │ │ │
│ │ │ (自定义轻客户端) │ │ │ │ (Ethereum方向) │ │ │
│ │ └─────────────────┘ │ │ └───────────────────┘ │ │
│ │ ┌─────────────────┐ │ │ ┌───────────────────┐ │ │
│ │ │ ICS-20 Transfer │ │ │ │ Burn-Mint Bridge │ │ │
│ │ │ ICS-27 ICA │ │ │ │ (通用方向) │ │ │
│ │ │ ICS-721 NFT │ │ │ └───────────────────┘ │ │
│ │ └─────────────────┘ │ │ ┌───────────────────┐ │ │
│ │ ┌─────────────────┐ │ │ │ Bridge Validator │ │ │
│ │ │ IBC Fee │ │ │ │ Set (多签验证) │ │ │
│ │ │ Middleware │ │ │ └───────────────────┘ │ │
│ │ └─────────────────┘ │ │ │ │
│ └─────────────────────────┘ └───────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Relayer 网络 │ │
│ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ │
│ │ │ Go Relayer │ │ Hermes │ │ MSG Relayer│ (自定义) │ │
│ │ └────────────┘ └────────────┘ └────────────┘ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 跨链 dApp 层 │ │
│ │ ┌─────────────────────┐ ┌─────────────────────┐ │ │
│ │ │ 跨链AI Agent市场 │ │ 跨链 DID 解析 │ │ │
│ │ └─────────────────────┘ └─────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
1.3 目标集成链
MSG Chain 规划与以下链建立 IBC 连接:
| 目标链 | Chain ID | IBC 版本 | 优先级 | 说明 |
|---|---|---|---|---|
| Cosmos Hub | cosmoshub-4 |
v2/v3 | P0 | 主路由枢纽,类似 ATOM 的 IBC 路由 |
| Osmosis | osmosis-1 |
v2/v3 | P0 | 跨链 DEX,类似 OSMO 的跨链 swap |
| Stride | stride-1 |
v2 | P1 | 跨链质押 |
| Neutron | neutron-1 |
v2 | P1 | 跨链合约调用 |
| Ethereum (EVM) | N/A | ICS-721/自定义 | P2 | 通过自定义桥接或 ICS-721 |
1.4 跨链数据流
X-MSG-Stub=true
用户 A (MSG Chain) 用户 B (Cosmos Hub)
│ │
│ 1. msgd tx ibc-transfer │
│ send-packet │
│ │
▼ │
┌─────────────┐ │
│ Escrow │ 2. IBC 数据包经 Relayer │
│ Account │ ──────────────────────────► │
└─────────────┘ │
▼
┌─────────────┐
│ Mint IBC │
│ Denom │
└─────────────┘
2. IBC协议基础
2.1 IBC 协议简介
IBC (Inter-Blockchain Communication) 是 Cosmos 生态的跨链通信标准,基于轻客户端验证和中继者网络实现无需信任的跨链交互。
与传统的跨链桥相比,IBC 的核心优势:
| 特性 | IBC | 传统桥 (Wormhole, Multisig) |
|---|---|---|
| 安全模型 | 轻客户端验证 | 验证者/多签信任 |
| 信任假设 | 无需信任 (trustless) | 部分信任 |
| 最终性 | 链上共识验证 | 依赖预言机 |
| 去中心化程度 | 完全去中心化 | 部分中心化 |
| 可扩展性 | 模块化,可组合 | 单体架构 |
2.2 IBC 核心概念
2.2.1 轻客户端 (Light Client)
IBC 轻客户端是一段运行在目标链上的代码,负责验证来源链的区块头。Cosmos 生态中最常见的是 Tendermint 轻客户端:
┌──────────────┐ ┌──────────────┐
│ Chain A │ │ Chain B │
│ │ │ │
│ ┌────────┐ │ Relayer │ ┌────────┐ │
│ │ Block │──┼─────────►│ │ LC │ │
│ │ Headers│ │ 提交区块头 │ │ Verify│ │
│ └────────┘ │ │ └────────┘ │
│ │ │ │
└──────────────┘ └──────────────┘
MSG Chain 计划支持以下轻客户端类型:
| 轻客户端类型 | 状态 | 说明 |
|---|---|---|
| Tendermint 客户端 | 规划中 | 标准 Cosmos IBC 兼容 |
| Dilithium-5 客户端 | 规划中 | MSG Chain 自定义,后量子安全 |
| Solo Machine 客户端 | 规划中 | 用于非 IBC 链连接 |
2.2.2 连接 (Connection) 与通道 (Channel)
Connection 和 Channel 是 IBC 的两层抽象:
Chain A Chain B
│ │
│ ┌── Connection (connID) ──┐ │
│ │ │ │
│ │ ┌─ Channel (chID) ──┐ │ │
│ │ │ Port: "transfer" │ │ │
│ │ │ Port: "wasm.xxx" │ │ │
│ │ └────────────────────┘ │ │
│ │ ┌─ Channel (chID) ──┐ │ │
│ │ │ Port: "ica" │ │ │
│ │ └────────────────────┘ │ │
│ └──────────────────────────┘ │
│ │
Connection 握手流程 (4步):
X-MSG-Stub=true
Chain A (msg-chain-1) Chain B (cosmoshub-4)
│ │
│ 1. ConnOpenInit │
│ ──────────────────────────────► │
│ │
│ 2. ConnOpenTry │
│ ◄────────────────────────────── │
│ │
│ 3. ConnOpenAck │
│ ──────────────────────────────► │
│ │
│ 4. ConnOpenConfirm │
│ ◄────────────────────────────── │
│ │
│ Connection 建立 │
Channel 握手流程 (4步):
X-MSG-Stub=true
Chain A (msg-chain-1) Chain B (cosmoshub-4)
│ │
│ 1. ChanOpenInit │
│ ──────────────────────────────► │
│ │
│ 2. ChanOpenTry │
│ ◄────────────────────────────── │
│ │
│ 3. ChanOpenAck │
│ ──────────────────────────────► │
│ │
│ 4. ChanOpenConfirm │
│ ◄────────────────────────────── │
│ │
│ Channel 建立 │
2.2.3 数据包生命周期 (Packet Lifecycle)
IBC 数据包的完整生命周期包括 4 个阶段:
X-MSG-Stub=true
发送端 (Source) 接收端 (Destination)
│ │
│ 1. sendPacket │
│ ──────────────────────────────► │
│ (数据包进入 outgoing queue) │
│ │
│ Relayer 转发 │
│ ◄═══════════════════════════════► │
│ │
│ 2. recvPacket│
│ ◄──────── │
│ │
│ 3. writeAcknowledgement │
│ ──────────────────────────────► │
│ │
│ 4. (timeout) (ack) │
│ 超时处理 确认处理 │
数据包数据结构:
// IBC 数据包标准结构
message Packet {
// 唯一序列号
uint64 sequence = 1;
// 来源链的端口和通道
string source_port = 2;
string source_channel = 3;
// 目标链的端口和通道
string destination_port = 4;
string destination_channel = 5;
// 原始数据 (opaque bytes)
bytes data = 6;
// 超时高度 (过期区块高度)
Height timeout_height = 7;
// 超时时间戳 (过期时间)
uint64 timeout_timestamp = 8;
}
message Height {
uint64 revision_number = 1;
uint64 revision_height = 2;
}
2.3 Relayer 架构
Relayer (中继者) 是 IBC 协议的关键基础设施组件,负责:
- 监控两条链的 IBC 事件
- 转发数据包、确认和超时
- 提交轻客户端更新
X-MSG-Stub=true
┌──────────────────────────────────────────────────┐
│ Relayer │
│ │
│ ┌──────────────┐ ┌──────────────────────┐ │
│ │ Chain Monitor │ │ Packet Processor │ │
│ │ - A链事件订阅 │ │ - 验证数据包 │ │
│ │ - B链事件订阅 │ │ - 签名交易 │ │
│ └──────────────┘ │ - 广播交易 │ │
│ └──────────────────────┘ │
│ ┌──────────────┐ ┌──────────────────────┐ │
│ │ Key Manager │ │ Fee Distributor │ │
│ │ - 中继者密钥 │ │ - Gas 管理 │ │
│ │ - 多链密钥 │ │ - 费用收取 │ │
│ └──────────────┘ └──────────────────────┘ │
└──────────────────────────────────────────────────┘
2.4 IBC 安全模型
IBC 的安全保证基于以下假设:
| 安全假设 | 风险等级 | 缓解措施 |
|---|---|---|
| 来源链共识安全 | 低 (DPoS) | 多种共识机制可选 |
| 轻客户端正确性 | 低 | 形式化验证 |
| Relayer 诚实性 | 不需要 | Relayer 不能造假,只能延迟 |
| 链未分叉 | 低 | IBC 需要最终性 |
| 密钥管理 | 中 | HSMs, 多签 |
与以太坊桥相比,IBC 不需要信任 Relayer——Relayer 无法窃取资金,最多只能造成审查(延迟交付)。
3. MSG Chain IBC规划
X-MSG-Stub=true: 本节所有内容均为规划阶段,尚未实现。
3.1 规划中的 IBC 版本
MSG Chain 计划基于 IBC v2 进行实现。IBC v2 相比 v1 的主要改进:
| 特性 | IBC v1 (Cosmos Hub) | IBC v2 (MSG Chain 计划) |
|---|---|---|
| 数据包格式 | Protobuf | Protobuf + 扩展元数据 |
| 轻客户端接口 | ClientState/ConsensusState | 更灵活的接口 |
| 模块化 | 基础 | 更好的中间件组合 |
| 性能 | 标准 | 优化的批处理 |
| 费用 | IBC Fee 可选 | 默认集成 |
3.2 Dilithium-5 轻客户端设计
X-MSG-Stub=true
MSG Chain 的 Dilithium-5 轻客户端是 IBC 实现的核心创新点。标准 Tendermint 轻客户端使用 Ed25519 签名,而 MSG Chain 需要支持 Dilithium-5 后量子签名。
3.2.1 轻客户端架构
// X-MSG-Stub=true: Dilithium-5 IBC 轻客户端设计
// 此代码为规划中的设计草案,尚未实现
/// Dilithium-5 轻客户端状态
pub struct DilithiumClientState {
/// 链 ID
pub chain_id: String,
/// 信任级别 (1/3 共识)
pub trust_level: Fraction,
/// 信任周期 ( trusting period )
pub trusting_period: Duration,
/// 解绑周期 ( unbonding period )
pub unbonding_period: Duration,
/// 最大时钟偏差
pub max_clock_drift: Duration,
/// 最新高度
pub latest_height: Height,
/// Dilithium-5 共识状态
pub consensus_state: DilithiumConsensusState,
}
/// Dilithium-5 共识状态
pub struct DilithiumConsensusState {
/// 验证者集公钥哈希 (Dilithium-5 聚合)
pub validator_set_hash: Vec<u8>,
/// Round-Robin + DAR 时间戳
pub timestamp: Timestamp,
/// 根哈希
pub root: Vec<u8>,
/// 下一个验证者集哈希
pub next_validators_hash: Vec<u8>,
}
/// Dilithium-5 轻客户端验证逻辑
pub fn verify_dilithium_misbehaviour(
client_state: &DilithiumClientState,
misbehaviour: &Misbehaviour,
) -> Result<(), IbcError> {
// 1. 验证两个冲突区块头都通过了 Dilithium-5 签名验证
verify_dilithium_header(&misbehaviour.header_1)?;
verify_dilithium_header(&misbehaviour.header_2)?;
// 2. 验证两个区块头在相同高度有不同哈希
if misbehaviour.header_1.height() != misbehaviour.header_2.height() {
return Err(IbcError::HeightMismatch);
}
if misbehaviour.header_1.hash() == misbehaviour.header_2.hash() {
return Err(IbcError::IdenticalHeaders);
}
// 3. 验证 Dilithium-5 签名来自验证者集
verify_dilithium_signatures(
&misbehaviour.header_1,
&client_state.consensus_state.validator_set_hash,
)?;
Ok(())
}
3.2.2 Dilithium-5 轻客户端与原版对比
| 特性 | Tendermint LC | Dilithium-5 LC |
|---|---|---|
| 签名算法 | Ed25519 | Dilithium-5 |
| 公钥大小 | 32 bytes | 1,312 bytes |
| 签名大小 | 64 bytes | 2,420 bytes |
| 验证速度 | 快速 | 中等 (~2x Ed25519) |
| 量子安全性 | 否 (易受 Shor 攻击) | 是 |
| 存储开销 | 低 | 较高 |
3.3 与 Cosmos Hub 的连接规划
3.3.1 连接流程
X-MSG-Stub=true
msg-chain-1 (MSG Chain) cosmoshub-4 (Cosmos Hub)
│ │
│ Step 1: 创建 Dilithium-5 客户端 │
│ msgd tx ibc client create │
│ ─────────────────────────────────► │
│ │
│ Step 2: Cosmos Hub 创建 Tendermint LC │
│ (Cosmos Hub 需要支持 Dilithium-5 验证) │
│ ◄───────────────────────────────── │
│ │
│ Step 3-6: Connection 握手 │
│ ◄═══════════════════════════════► │
│ │
│ Step 7-10: Channel 握手 │
│ ◄═══════════════════════════════► │
│ │
│ ✓ 连接建立,可开始资产转移 │
3.3.2 CLI 命令 (规划中)
# X-MSG-Stub=true: 以下命令为规划中的 CLI,尚未支持
# 1. 创建 IBC 客户端 (在 MSG Chain 上)
msgd tx ibc client create \
--client-type dilithium5 \
--chain-id cosmoshub-4 \
--node https://rpc.cosmoshub-4.com:26657 \
--from my-key
# 2. 创建连接
msgd tx ibc connection open-init \
--client-id 07-dilithium-0 \
--counterparty-client-id 07-tendermint-0 \
--from my-key \
--gas-prices 1000000000umsg
# 3. 创建通道 (ICS-20 转账)
msgd tx ibc channel open-init \
--port transfer \
--channel-ordering unordered \
--connection-id connection-0 \
--version "ics20-1" \
--from my-key \
--gas-prices 1000000000umsg
3.4 规划中的通道类型
| 通道类型 | 标准 | 用途 | 优先级 |
|---|---|---|---|
| 代币转账 | ICS-20 | 跨链 MSG 转移 | P0 |
| Interchain Accounts | ICS-27 | 跨链账户控制 | P1 |
| NFT 转移 | ICS-721 | 跨链 Agent NFT | P2 |
| IBC Fee | ICS-29 | Relayer 费用 | P1 |
| 自定义桥接 | 自定义 | 非 IBC 链桥接 | P2 |
3.5 开发状态和时间线
| 里程碑 | 预计时间 | 状态 | 依赖 |
|---|---|---|---|
| Dilithium-5 IBC 客户端规范 | Q3 2026 | 规划中 | IBC v2 规范 |
| Dilithium-5 轻客户端实现 | Q4 2026 | 未开始 | Cosmos SDK IBC 模块 |
| Connection 握手测试 | Q1 2027 | 未开始 | 测试网部署 |
| ICS-20 集成 | Q1 2027 | 未开始 | 连接建立 |
| Testnet 跨链转账 | Q2 2027 | 未开始 | ICS-20 就绪 |
| 自定义桥接合约 | Q3 2027 | 未开始 | CosmWasm + IBC |
| 主网跨链上线 | Q4 2027 | 未开始 | 以上全部 |
4. ICS-20代币转移
X-MSG-Stub=true: ICS-20 代币转移功能尚未实现,以下为规划中的使用方式。
4.1 IBC Denom 格式
通过 IBC 转移的代币使用以下格式标识:
ibc/{hash}
其中 {hash} 是 {channel_id}/{denom} 的 SHA256 哈希。
示例: ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2
4.2 IBC 转账 CLI 命令
4.2.1 从 MSG Chain 转出到 Cosmos Hub
# X-MSG-Stub=true: 将 100 MSG 从 MSG Chain 发送到 Cosmos Hub
msgd tx ibc-transfer transfer \
transfer \
channel-0 \
cosmos1qypqxpq9kcrn2c9afea5lq35ef37c5x7abcdef \
100000000000000000000umsg \
--packet-timeout-height "0-100000" \
--packet-timeout-timestamp "0" \
--from my-msg-key \
--gas-prices 1000000000umsg \
--chain-id msg-chain-1 \
--node https://rpc.msgchain.org:26657
# 响应:
# code: 0
# txhash: ABCDEF1234567890...
# 消息: IBC 转账已发起,序列号: 1
4.2.2 从 Cosmos Hub 转入 MSG Chain
# X-MSG-Stub=true: 在 Cosmos Hub 上执行
gaiad tx ibc-transfer transfer \
transfer \
channel-{msg-chain-id} \
msg1qypqxpq9kcrn2c9afea5lq35ef37c5x7jqylz3 \
1000000uatom \
--packet-timeout-height "0-50000" \
--from my-cosmos-key \
--gas-prices 0.025uatom \
--chain-id cosmoshub-4 \
--node https://rpc.cosmoshub-4.com:26657
4.2.3 查询 IBC 转移状态
# X-MSG-Stub=true: 查询 IBC 数据包状态
msgd query ibc-transfer denom-trace 27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2
# 响应:
# denom_trace:
# path: transfer/channel-0
# base_denom: uatom
# 查询待处理的数据包
msgd query ibc channel pending-send \
transfer \
channel-0 \
--node https://rpc.msgchain.org:26657
# 查询已确认的数据包
msgd query ibc channel packet-ack \
transfer \
channel-0 \
1 \
--node https://rpc.msgchain.org:26657
4.3 Channel 创建命令
# X-MSG-Stub=true: 在 MSG Chain 上创建 IBC 转账通道
msgd tx ibc channel open-init \
--port transfer \
--channel-ordering unordered \
--connection-id connection-0 \
--version "ics20-1" \
--from my-key \
--gas-prices 1000000000umsg \
--chain-id msg-chain-1
# 确认通道状态
msgd query ibc channel end transfer channel-0
4.4 托管账户管理
在 ICS-20 中,代币转移涉及托管账户 (Escrow Account)。当代币从 MSG Chain 转出时:
- MSG Chain 上的代币被锁定在托管模块
- IBC 数据包被发送到目标链
- 目标链在接收端铸造等量的 IBC 代币
- 当转回时,IBC 代币被销毁,MSG Chain 解锁原代币
X-MSG-Stub=true
MSG Chain (发送) Cosmos Hub (接收)
│ │
│ ┌──────────────────┐ │
│ │ Escrow Module │ │
│ │ (锁定 100 MSG) │ │
│ └──────────────────┘ │
│ │ │
│ │ sendPacket │
│ └─────────────────────────► │
│ │
│ ┌────────┐ │
│ │ Mint │ │
│ │ IBC/MSG│ │
│ └────────┘ │
│ │
│ (转回时) │
│ ◄────────────────────────────────── │
│ │
│ ┌──────────────────┐ │
│ │ 解锁 100 MSG │ │
│ └──────────────────┘ │
查询托管余额:
# X-MSG-Stub=true: 查询 IBC 托管余额
msgd query ibc-transfer escrow-address transfer channel-0
# 响应:
# escrow_address: msg1xude6s5c6m73lfwd0576tgvs3v9wf8v6xtx3ht
# 查询托管地址余额
msgd query bank balances msg1xude6s5c6m73lfwd0576tgvs3v9wf8v6xtx3ht \
--node https://rpc.msgchain.org:26657
4.5 完整 CosmWasm 合约: IBC 代币管理
X-MSG-Stub=true: 以下合约代码为规划中的设计,展示了 MSG Chain CosmWasm 合约与 IBC 的集成方式。
4.5.1 Cargo.toml
[package]
name = "msg-ibc-token-manager"
version = "1.0.0"
edition = "2021"
description = "MSG Chain IBC Token Management Contract"
[lib]
crate-type = ["cdylib", "rlib"]
[profile.release]
opt-level = 3
debug = false
lto = true
debug-assertions = false
codegen-units = 1
panic = "abort"
overflow-checks = true
[dependencies]
cosmwasm-std = "1.5"
cosmwasm-storage = "1.5"
cw-storage-plus = "1.2"
cw2 = "1.1"
cw-utils = "1.0"
cw20 = "1.1"
schemars = "0.8"
serde = { version = "1.0", features = ["derive"] }
thiserror = "1.0"
uint = "0.9"
[dev-dependencies]
cosmwasm-vm = "1.5"
cw-multi-test = "0.18"
4.5.2 src/msg.rs
use cosmwasm_schema::cw_serde;
use cosmwasm_std::{Binary, Uint128};
#[cw_serde]
pub struct InstantiateMsg {
pub admin: String,
pub ibc_transfer_module: String,
pub supported_channels: Vec<String>,
}
#[cw_serde]
pub enum ExecuteMsg {
/// 发起 IBC 转账
InitiateIBCTransfer {
channel_id: String,
recipient: String,
amount: Uint128,
denom: String,
memo: Option<String>,
},
/// 注册 IBC 通道
RegisterChannel {
channel_id: String,
chain_name: String,
ibc_denom: String,
},
/// 暂停 IBC 转账
PauseTransfer {},
/// 恢复 IBC 转账
ResumeTransfer {},
/// 设置最大转账限额
SetTransferLimit {
channel_id: String,
daily_limit: Uint128,
},
}
#[cw_serde]
pub enum QueryMsg {
GetChannelInfo { channel_id: String },
ListChannels {},
GetEscrowBalance { channel_id: String },
GetDailyTransferVolume { channel_id: String },
IsPaused {},
}
#[cw_serde]
pub struct ChannelInfo {
pub channel_id: String,
pub chain_name: String,
pub ibc_denom: String,
pub daily_limit: Uint128,
pub today_volume: Uint128,
pub last_reset: u64,
pub active: bool,
}
#[cw_serde]
pub struct IBCTransferRequest {
pub source_channel: String,
pub recipient: String,
pub amount: Uint128,
pub denom: String,
pub memo: Option<String>,
pub sender: String,
}
4.5.3 src/state.rs
use cosmwasm_schema::cw_serde;
use cw_storage_plus::{Item, Map};
use cosmwasm_std::Uint128;
use crate::msg::ChannelInfo;
#[cw_serde]
pub struct Config {
pub admin: String,
pub ibc_transfer_module: String,
pub paused: bool,
}
pub const CONFIG: Item<Config> = Item::new("config");
pub const CHANNELS: Map<&str, ChannelInfo> = Map::new("channels");
pub const TRANSFER_VOLUME: Map<&str, Uint128> = Map::new("transfer_volume");
pub const DAILY_RESET: Map<&str, u64> = Map::new("daily_reset");
4.5.4 src/error.rs
use cosmwasm_std::StdError;
use thiserror::Error;
#[derive(Error, Debug, PartialEq)]
pub enum ContractError {
#[error("{0}")]
Std(#[from] StdError),
#[error("Unauthorized")]
Unauthorized {},
#[error("IBC transfers are paused")]
Paused {},
#[error("Channel {channel_id} not found")]
ChannelNotFound { channel_id: String },
#[error("Channel {channel_id} is inactive")]
ChannelInactive { channel_id: String },
#[error("Transfer amount exceeds daily limit for {channel_id}")]
DailyLimitExceeded { channel_id: String },
#[error("Channel {channel_id} already registered")]
ChannelAlreadyExists { channel_id: String },
#[error("Insufficient balance for IBC transfer")]
InsufficientBalance {},
}
4.5.5 src/contract.rs
use cosmwasm_std::{
entry_point, to_binary, Binary, Deps, DepsMut, Env, IbcMsg,
IbcTimeout, IbcTimeoutBlock, MessageInfo, Response, StdResult, Uint128,
};
use cw2::set_contract_version;
use cw_utils::one_coin;
use crate::error::ContractError;
use crate::msg::{
ChannelInfo, ExecuteMsg, InstantiateMsg, QueryMsg,
};
use crate::state::{Config, CONFIG, CHANNELS, DAILY_RESET, TRANSFER_VOLUME};
const CONTRACT_NAME: &str = "msg-ibc-token-manager";
const CONTRACT_VERSION: &str = "1.0.0";
#[entry_point]
pub fn instantiate(
deps: DepsMut,
_env: Env,
_info: MessageInfo,
msg: InstantiateMsg,
) -> StdResult<Response> {
set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
let config = Config {
admin: msg.admin,
ibc_transfer_module: msg.ibc_transfer_module,
paused: false,
};
CONFIG.save(deps.storage, &config)?;
for channel_id in msg.supported_channels {
let info = ChannelInfo {
channel_id: channel_id.clone(),
chain_name: String::new(),
ibc_denom: String::new(),
daily_limit: Uint128::zero(),
today_volume: Uint128::zero(),
last_reset: 0,
active: true,
};
CHANNELS.save(deps.storage, &channel_id, &info)?;
}
Ok(Response::new()
.add_attribute("method", "instantiate")
.add_attribute("admin", msg.admin))
}
#[entry_point]
pub fn execute(
deps: DepsMut,
env: Env,
info: MessageInfo,
msg: ExecuteMsg,
) -> Result<Response, ContractError> {
match msg {
ExecuteMsg::InitiateIBCTransfer {
channel_id,
recipient,
amount,
denom,
memo,
} => execute_initiate_ibc_transfer(
deps, env, info, channel_id, recipient, amount, denom, memo,
),
ExecuteMsg::RegisterChannel {
channel_id,
chain_name,
ibc_denom,
} => execute_register_channel(deps, info, channel_id, chain_name, ibc_denom),
ExecuteMsg::PauseTransfer {} => execute_pause(deps, info),
ExecuteMsg::ResumeTransfer {} => execute_resume(deps, info),
ExecuteMsg::SetTransferLimit {
channel_id,
daily_limit,
} => execute_set_limit(deps, info, channel_id, daily_limit),
}
}
fn execute_initiate_ibc_transfer(
deps: DepsMut,
env: Env,
info: MessageInfo,
channel_id: String,
recipient: String,
amount: Uint128,
denom: String,
memo: Option<String>,
) -> Result<Response, ContractError> {
let config = CONFIG.load(deps.storage)?;
if config.paused {
return Err(ContractError::Paused {});
}
let mut channel = CHANNELS
.load(deps.storage, &channel_id)
.map_err(|_| ContractError::ChannelNotFound {
channel_id: channel_id.clone(),
})?;
if !channel.active {
return Err(ContractError::ChannelInactive { channel_id });
}
// 检查每日限额
let now = env.block.time.seconds();
let today = now / 86400;
let last_reset = DAILY_RESET.load(deps.storage, &channel_id).unwrap_or(0);
if last_reset < today {
DAILY_RESET.save(deps.storage, &channel_id, &today)?;
TRANSFER_VOLUME.save(deps.storage, &channel_id, &Uint128::zero())?;
channel.today_volume = Uint128::zero();
}
let current_volume = TRANSFER_VOLUME
.load(deps.storage, &channel_id)
.unwrap_or_default();
if !channel.daily_limit.is_zero() && current_volume + amount > channel.daily_limit {
return Err(ContractError::DailyLimitExceeded { channel_id });
}
TRANSFER_VOLUME
.save(deps.storage, &channel_id, &(current_volume + amount))?;
channel.today_volume = current_volume + amount;
CHANNELS.save(deps.storage, &channel_id, &channel)?;
// 构建 IBC 转账消息
let ibc_msg = IbcMsg::Transfer {
channel_id: channel_id.clone(),
to_address: recipient.clone(),
amount: cosmwasm_std::Coin {
denom: denom.clone(),
amount,
},
timeout: IbcTimeout::with_block(IbcTimeoutBlock {
revision: 0,
height: env.block.height + 10000,
}),
};
let mut response = Response::new()
.add_message(ibc_msg)
.add_attribute("method", "initiate_ibc_transfer")
.add_attribute("channel", &channel_id)
.add_attribute("recipient", &recipient)
.add_attribute("amount", amount.to_string())
.add_attribute("denom", &denom);
if let Some(m) = memo {
response = response.add_attribute("memo", m);
}
Ok(response)
}
fn execute_register_channel(
deps: DepsMut,
info: MessageInfo,
channel_id: String,
chain_name: String,
ibc_denom: String,
) -> Result<Response, ContractError> {
let config = CONFIG.load(deps.storage)?;
if info.sender.to_string() != config.admin {
return Err(ContractError::Unauthorized {});
}
if CHANNELS.has(deps.storage, &channel_id) {
return Err(ContractError::ChannelAlreadyExists { channel_id });
}
let channel = ChannelInfo {
channel_id: channel_id.clone(),
chain_name,
ibc_denom,
daily_limit: Uint128::zero(),
today_volume: Uint128::zero(),
last_reset: 0,
active: true,
};
CHANNELS.save(deps.storage, &channel_id, &channel)?;
Ok(Response::new()
.add_attribute("method", "register_channel")
.add_attribute("channel_id", &channel_id))
}
fn execute_pause(
deps: DepsMut,
info: MessageInfo,
) -> Result<Response, ContractError> {
let mut config = CONFIG.load(deps.storage)?;
if info.sender.to_string() != config.admin {
return Err(ContractError::Unauthorized {});
}
config.paused = true;
CONFIG.save(deps.storage, &config)?;
Ok(Response::new().add_attribute("method", "pause"))
}
fn execute_resume(
deps: DepsMut,
info: MessageInfo,
) -> Result<Response, ContractError> {
let mut config = CONFIG.load(deps.storage)?;
if info.sender.to_string() != config.admin {
return Err(ContractError::Unauthorized {});
}
config.paused = false;
CONFIG.save(deps.storage, &config)?;
Ok(Response::new().add_attribute("method", "resume"))
}
fn execute_set_limit(
deps: DepsMut,
info: MessageInfo,
channel_id: String,
daily_limit: Uint128,
) -> Result<Response, ContractError> {
let config = CONFIG.load(deps.storage)?;
if info.sender.to_string() != config.admin {
return Err(ContractError::Unauthorized {});
}
let mut channel = CHANNELS
.load(deps.storage, &channel_id)
.map_err(|_| ContractError::ChannelNotFound {
channel_id: channel_id.clone(),
})?;
channel.daily_limit = daily_limit;
CHANNELS.save(deps.storage, &channel_id, &channel)?;
Ok(Response::new()
.add_attribute("method", "set_limit")
.add_attribute("channel_id", &channel_id)
.add_attribute("daily_limit", daily_limit.to_string()))
}
#[entry_point]
pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
match msg {
QueryMsg::GetChannelInfo { channel_id } => {
to_binary(&query_channel_info(deps, channel_id)?)
}
QueryMsg::ListChannels {} => to_binary(&query_list_channels(deps)?),
QueryMsg::GetEscrowBalance { channel_id: _ } => {
to_binary(&query_escrow_balance(deps)?)
}
QueryMsg::GetDailyTransferVolume { channel_id } => {
to_binary(&query_daily_volume(deps, channel_id)?)
}
QueryMsg::IsPaused {} => to_binary(&CONFIG.load(deps.storage)?.paused),
}
}
fn query_channel_info(deps: Deps, channel_id: String) -> StdResult<ChannelInfo> {
CHANNELS.load(deps.storage, &channel_id)
}
fn query_list_channels(deps: Deps) -> StdResult<Vec<ChannelInfo>> {
let channels: StdResult<Vec<_>> = CHANNELS
.range(deps.storage, None, None, cosmwasm_std::Order::Ascending)
.map(|r| r.map(|(_, v)| v))
.collect();
channels
}
fn query_escrow_balance(deps: Deps) -> StdResult<String> {
// 查询托管余额
Ok("escrow_balance_query".to_string())
}
fn query_daily_volume(deps: Deps, channel_id: String) -> StdResult<Uint128> {
TRANSFER_VOLUME
.load(deps.storage, &channel_id)
.or(Ok(Uint128::zero()))
}
4.6 CosmJS IBC 集成代码 (TypeScript)
// X-MSG-Stub=true: CosmJS IBC 转账示例
import { SigningStargateClient } from "@cosmjs/stargate";
import { coins } from "@cosmjs/launchpad";
import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing";
// MSG Chain 使用 Dilithium-5 签名,需要自定义签名适配
// import { Dilithium5Wallet } from "@msgchain/crypto";
const MSG_CHAIN_RPC = "https://rpc.msgchain.org:26657";
const MSG_CHAIN_ID = "msg-chain-1";
const IBC_TRANSFER_PORT = "transfer";
const IBC_CHANNEL = "channel-0";
interface IBCTransferParams {
fromAddress: string;
recipientAddress: string;
transferAmount: { denom: string; amount: string };
sourcePort: string;
sourceChannel: string;
timeoutHeight: { revisionHeight: number; revisionNumber: number };
}
/**
* 发起 IBC 代币转账
*
* 注意: MSG Chain 使用 Dilithium-5 签名。
* 在 Dilithium-5 钱包适配器完成前,此代码为规划示例。
*/
async function initiateIBCTransfer(
mnemonic: string,
params: IBCTransferParams
): Promise<string> {
// TODO: 替换为 Dilithium5Wallet
// const wallet = await Dilithium5Wallet.fromMnemonic(mnemonic);
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(mnemonic, {
prefix: "msg",
});
const client = await SigningStargateClient.connectWithSigner(
MSG_CHAIN_RPC,
wallet
);
const fee = {
amount: coins(5000, "umsg"),
gas: "200000",
};
const memo = "IBC transfer from MSG Chain";
const result = await client.sendIbcTokens(
params.fromAddress,
params.recipientAddress,
params.transferAmount,
params.sourcePort,
params.sourceChannel,
params.timeoutHeight,
undefined, // timeoutTimestamp
fee,
memo
);
console.log(`IBC 转账已提交,交易哈希: ${result.transactionHash}`);
return result.transactionHash;
}
/**
* 查询 IBC 通道信息
*/
async function queryIBCChannel(
channelId: string = IBC_CHANNEL
): Promise<any> {
const client = await SigningStargateClient.connect(MSG_CHAIN_RPC);
const channel = await client.ibcChannel(
IBC_TRANSFER_PORT,
channelId
);
return channel;
}
/**
* 查询 IBC 托管余额
*/
async function queryEscrowBalance(
channelId: string = IBC_CHANNEL
): Promise<any> {
const client = await SigningStargateClient.connect(MSG_CHAIN_RPC);
const escrowAddress = await client.ibcEscrowAddress(
IBC_TRANSFER_PORT,
channelId
);
const balance = await client.getBalance(escrowAddress, "umsg");
return { escrowAddress, balance };
}
/**
* 查询待处理的数据包
*/
async function queryPendingPackets(
channelId: string = IBC_CHANNEL
): Promise<any> {
const client = await SigningStargateClient.connect(MSG_CHAIN_RPC);
const packets = await client.ibcPendingPackets(
IBC_TRANSFER_PORT,
channelId
);
return packets;
}
// 使用示例
async function main() {
const mnemonic =
"[未公开凭证]";
const txHash = await initiateIBCTransfer(mnemonic, {
fromAddress: "msg1qypqxpq9kcrn2c9afea5lq35ef37c5x7jqylz3",
recipientAddress: "cosmos1qypqxpq9kcrn2c9afea5lq35ef37c5x7abcdef",
transferAmount: { denom: "umsg", amount: "100000000000000000000" },
sourcePort: IBC_TRANSFER_PORT,
sourceChannel: IBC_CHANNEL,
timeoutHeight: {
revisionHeight: 100000,
revisionNumber: 0,
},
});
console.log(`转账完成: https://explorer.msgchain.org/tx/${txHash}`);
}
5. Relayer配置
X-MSG-Stub=true: Relayer 配置和使用方法为规划中的内容。
5.1 Go Relayer 配置 (rly)
5.1.1 Go Relayer 安装
# 安装 Go Relayer (rly)
git clone https://github.com/cosmos/relayer.git
cd relayer
make install
# 验证安装
rly version
5.1.2 初始化配置
# X-MSG-Stub=true: 初始化 rly 配置目录
rly config init
# 配置文件路径: ~/.relayer/config/config.yaml
# 添加 MSG Chain 配置
rly chains add
5.1.3 完整配置文件
# X-MSG-Stub=true: ~/.relayer/config/config.yaml
global:
api-listen-addr: :5183
timeout: 30s
memo: "MSG Chain Relayer"
light-cache-size: 20
chains:
msg-chain-1:
type: cosmos
value:
key: relayer-key-msg
chain-id: msg-chain-1
rpc-addr: https://rpc.msgchain.org:26657
account-prefix: msg
keyring-backend: file
gas-adjustment: 1.3
gas-prices: 1000000000umsg
min-gas-amount: 1000
debug: false
timeout: 30s
block-timeout: ""
output-format: json
sign-mode: direct
# Dilithium-5 签名支持 (自定义)
# signing-algorithm: dilithium5
extra-codecs: []
cosmoshub-4:
type: cosmos
value:
key: relayer-key-hub
chain-id: cosmoshub-4
rpc-addr: https://rpc.cosmoshub-4.com:26657
account-prefix: cosmos
keyring-backend: file
gas-adjustment: 1.3
gas-prices: 0.025uatom
min-gas-amount: 1000
debug: false
timeout: 30s
block-timeout: ""
output-format: json
sign-mode: direct
extra-codecs: []
paths:
msg-hub:
src:
chain-id: msg-chain-1
client-id: 07-dilithium-0
connection-id: connection-0
dst:
chain-id: cosmoshub-4
client-id: 07-tendermint-0
connection-id: connection-0
src-channel-filter:
rule: allowlist
channel-list:
- transfer
- wasm.msg1c0m3r4y
5.1.4 启动 Relayer
# X-MSG-Stub=true: Relayer 操作
# 添加密钥
rly keys add msg-chain-1 relayer-key-msg
# 输入助记词或生成新密钥
# 创建 IBC 客户端
rly clients create msg-hub
# 创建连接
rly connection create msg-hub
# 创建通道
rly channel create msg-hub --src-port transfer --dst-port transfer --order unordered --version ics20-1
# 启动 Relayer (持续运行)
rly start msg-hub
# 查看统计
rly status
5.2 Hermes 配置
Hermes 是另一个流行的 IBC Relayer 实现,Rust 编写,性能更高。
5.2.1 Hermes 安装
# 安装 Hermes
git clone https://github.com/informalsystems/hermes.git
cd hermes
cargo build --release
# 获取二进制
cp target/release/hermes ~/.local/bin/
# 验证
hermes version
5.2.2 Hermes 配置
# X-MSG-Stub=true: ~/.hermes/config.toml
[global]
log_level = "info"
[telemetry]
enabled = true
host = "127.0.0.1"
port = 3001
[[chains]]
id = "msg-chain-1"
rpc_addr = "https://rpc.msgchain.org:26657"
grpc_addr = "https://grpc.msgchain.org:9090"
websocket_addr = "wss://rpc.msgchain.org:26657/websocket"
rpc_timeout = "30s"
account_prefix = "msg"
key_name = "hermes-msg-key"
store_prefix = "ibc"
# 使用 Dilithium-5 签名
# signing_algorithm = "dilithium5"
gas_price = { price = 0.025, denom = "umsg" }
gas_adjustment = 1.3
max_msg_num = 30
max_tx_size = 2097152
clock_drift = "5s"
trusting_period = "14days"
trust_threshold = { numerator = "1", denominator = "3" }
[[chains]]
id = "cosmoshub-4"
rpc_addr = "https://rpc.cosmoshub-4.com:26657"
grpc_addr = "https://grpc.cosmoshub-4.com:9090"
websocket_addr = "wss://rpc.cosmoshub-4.com:26657/websocket"
rpc_timeout = "30s"
account_prefix = "cosmos"
key_name = "hermes-hub-key"
store_prefix = "ibc"
gas_price = { price = 0.025, denom = "uatom" }
gas_adjustment = 1.3
max_msg_num = 30
max_tx_size = 2097152
clock_drift = "5s"
trusting_period = "14days"
trust_threshold = { numerator = "1", denominator = "3" }
5.2.3 Hermes 使用命令
# X-MSG-Stub=true: Hermes 操作
# 添加密钥
hermes keys add --chain msg-chain-1 --key-name hermes-msg-key
# 创建连接
hermes create connection --a-chain msg-chain-1 --b-chain cosmoshub-4
# 创建通道
hermes create channel \
--a-chain msg-chain-1 \
--a-port transfer \
--b-port transfer \
--a-connection connection-0 \
--channel-version ics20-1
# 启动 Hermes
hermes start
# 查询通道状态
hermes query packet pending \
--chain msg-chain-1 \
--port transfer \
--channel channel-0
5.3 Docker Compose Relayer 部署
# X-MSG-Stub=true: docker-compose.yml
version: "3.8"
services:
relayer-rly:
image: ghcr.io/cosmos/relayer:latest
container_name: msg-chain-relayer
restart: unless-stopped
volumes:
- ./relayer-config:/home/relayer/.relayer
- ./relayer-keys:/home/relayer/.relayer/keys
command: ["rly", "start", "msg-hub"]
ports:
- "5183:5183"
environment:
- RLY_API_LISTEN_ADDR=:5183
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
relayer-hermes:
image: ghcr.io/informalsystems/hermes:latest
container_name: msg-chain-hermes
restart: unless-stopped
volumes:
- ./hermes-config:/home/hermes/.hermes
- ./hermes-keys:/home/hermes/.hermes/keys
command: ["hermes", "start"]
ports:
- "3001:3001"
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
relayer-monitor:
image: prom/prometheus:latest
container_name: relayer-monitor
restart: unless-stopped
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- ./prometheus-data:/prometheus
ports:
- "9090:9090"
command:
- "--config.file=/etc/prometheus/prometheus.yml"
grafana:
image: grafana/grafana:latest
container_name: relayer-grafana
restart: unless-stopped
ports:
- "3000:3000"
volumes:
- ./grafana-data:/var/lib/grafana
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin123
5.4 Relayer 密钥管理
# X-MSG-Stub=true: 密钥管理
# 创建新的 Relayer 密钥
msgd keys add relayer-key-1 \
--keyring-backend file \
--keyring-dir ~/.relayer/keys/msg-chain-1
# 导出密钥 (用于备份)
msgd keys export relayer-key-1 \
--keyring-backend file \
--keyring-dir ~/.relayer/keys/msg-chain-1 \
> relayer-key-1.backup
# 导入密钥
msgd keys import relayer-key-1 \
relayer-key-1.backup \
--keyring-backend file
# 查询密钥地址
msgd keys show relayer-key-1 \
--keyring-backend file \
--address
# 为 Relayer 充值 Gas 费用
msgd tx bank send my-main-wallet \
msg1relayeraddress... \
100000000000000000000umsg \
--gas-prices 1000000000umsg
5.5 数据包中继监控
# X-MSG-Stub=true: 监控 IBC 数据包
# 查询待发送数据包
msgd query ibc channel pending-send \
transfer channel-0 \
--node https://rpc.msgchain.org:26657
# 查询待接收数据包
msgd query ibc channel packet-commitment \
transfer channel-0 \
--node https://rpc.msgchain.org:26657
# 查询未确认的确认
msgd query ibc channel unreceived-acks \
transfer channel-0 \
1 \
--node https://rpc.msgchain.org:26657
# 查询未收到的数据包
msgd query ibc channel unreceived-packets \
transfer channel-0 \
1 \
--node https://rpc.msgchain.org:26657
# 持续监控 (使用 watch)
watch -n 10 'msgd query ibc channel pending-send transfer channel-0'
5.6 Relayer 经济学
Relayer 需要维护成本(Gas 费),因此需要经济激励:
| 成本项目 | 估算 (每日) | 说明 |
|---|---|---|
| B 链客户端更新 | 5-20 MSG | 每 ~100 个区块更新一次 LC |
| 数据包转发 | 0.5-5 MSG/包 | 取决于数据包大小 |
| 握手费用 | 10-50 MSG | 仅首次建立连接时 |
| 总估算 | 50-200 MSG/日 | 正常运行的 Relayer |
费用回收机制:
- IBC Fee 中间件 (ICS-29) — 规划中
- 可选收件人支付模式
- 超时惩罚保证金
6. 跨链合约开发
X-MSG-Stub=true: 本节所有合约代码为规划中的 IBC 集成示例。
6.1 IBC 启用合约模板
CosmWasm 合约可以注册为 IBC 模块,通过端口绑定直接发送和接收 IBC 数据包。
6.1.1 合约结构
msg-ibc-contract/
├── Cargo.toml
├── src/
│ ├── lib.rs
│ ├── contract.rs # CosmosMsg 入口
│ ├── msg.rs # 消息类型
│ ├── state.rs # 状态存储
│ ├── error.rs # 错误类型
│ ├── ibc.rs # IBC 入口点
│ └── helpers.rs
├── examples/schema.rs
└── tests/integration.rs
6.1.2 src/ibc.rs — IBC 入口点
use cosmwasm_std::{
entry_point, from_binary, to_binary, Binary, DepsMut, Env,
IbcBasicResponse, IbcChannel, IbcChannelCloseMsg,
IbcChannelConnectMsg, IbcChannelOpenMsg, IbcOrder,
IbcPacketAckMsg, IbcPacketReceiveMsg, IbcPacketTimeoutMsg,
IbcReceiveResponse, StdError, StdResult,
};
use crate::error::ContractError;
use crate::msg::{
AcknowledgementMsg, CrossChainQueryMsg, CrossChainResponse,
IbcExecuteMsg,
};
use crate::state::{CHANNEL_STATE, ChannelState};
/// 通道打开验证
#[entry_point]
pub fn ibc_channel_open(
_deps: DepsMut,
_env: Env,
msg: IbcChannelOpenMsg,
) -> StdResult<()> {
let channel = msg.channel();
let ordering = channel.ordering;
// MSG Chain 支持有序 (ORDERED) 和无序 (UNORDERED) 通道
if ordering != IbcOrder::Ordered && ordering != IbcOrder::Unordered {
return Err(StdError::generic_err(
"Channel ordering must be Ordered or Unordered",
));
}
// 验证版本
let expected_version = "msg-ibc-v1";
if channel.version != expected_version {
return Err(StdError::generic_err(format!(
"Unsupported IBC version: {}, expected: {}",
channel.version, expected_version
)));
}
Ok(())
}
/// 通道连接确认
#[entry_point]
pub fn ibc_channel_connect(
deps: DepsMut,
_env: Env,
msg: IbcChannelConnectMsg,
) -> StdResult<IbcBasicResponse> {
let channel: IbcChannel = msg.channel().clone();
// 保存通道状态
let state = ChannelState {
endpoint: channel.endpoint().clone(),
counterparty_endpoint: channel.counterparty_endpoint().clone(),
ordering: channel.ordering,
version: channel.version.clone(),
active: true,
};
CHANNEL_STATE.save(deps.storage, channel.endpoint().channel_id.as_str(), &state)?;
Ok(IbcBasicResponse::new()
.add_attribute("method", "ibc_channel_connect")
.add_attribute("channel_id", &channel.endpoint().channel_id))
}
/// 通道关闭处理
#[entry_point]
pub fn ibc_channel_close(
deps: DepsMut,
_env: Env,
msg: IbcChannelCloseMsg,
) -> StdResult<IbcBasicResponse> {
let channel = msg.channel();
let channel_id = channel.endpoint().channel_id.as_str();
// 标记通道为关闭
if let Ok(mut state) = CHANNEL_STATE.load(deps.storage, channel_id) {
state.active = false;
CHANNEL_STATE.save(deps.storage, channel_id, &state)?;
}
Ok(IbcBasicResponse::new()
.add_attribute("method", "ibc_channel_close")
.add_attribute("channel_id", channel_id))
}
/// 接收 IBC 数据包
#[entry_point]
pub fn ibc_packet_receive(
deps: DepsMut,
env: Env,
msg: IbcPacketReceiveMsg,
) -> Result<IbcReceiveResponse, Never> {
let packet = msg.packet;
let msg: IbcExecuteMsg = from_binary(&packet.data).map_err(|e| {
let ack = AcknowledgementMsg::Error(format!("Failed to parse: {}", e));
IbcReceiveResponse::new()
.set_ack(ack)
.add_attribute("parse_error", e.to_string())
})?;
// 处理 IBC 消息
let result = handle_ibc_message(deps, env, packet, msg);
match result {
Ok(response) => Ok(response),
Err(err) => {
let ack = AcknowledgementMsg::Error(err.to_string());
Ok(IbcReceiveResponse::new()
.set_ack(ack)
.add_attribute("error", err.to_string()))
}
}
}
/// 处理不同的 IBC 消息类型
fn handle_ibc_message(
deps: DepsMut,
env: Env,
packet: cosmwasm_std::IbcPacket,
msg: IbcExecuteMsg,
) -> Result<IbcReceiveResponse, ContractError> {
match msg {
IbcExecuteMsg::CrossChainQuery { query_type, params } => {
handle_cross_chain_query(deps, env, packet, query_type, params)
}
IbcExecuteMsg::CrossChainTransfer {
recipient,
amount,
denom,
} => handle_cross_chain_transfer(deps, env, packet, recipient, amount, denom),
IbcExecuteMsg::UpdateState {
key,
value,
} => handle_state_update(deps, env, packet, key, value),
}
}
fn handle_cross_chain_query(
_deps: DepsMut,
_env: Env,
_packet: cosmwasm_std::IbcPacket,
query_type: String,
params: Binary,
) -> Result<IbcReceiveResponse, ContractError> {
// 处理跨链查询
let response_data = CrossChainResponse {
query_type: query_type.clone(),
data: params,
success: true,
};
let ack = AcknowledgementMsg::Ok(Binary::from(
to_binary(&response_data).unwrap().as_slice(),
));
Ok(IbcReceiveResponse::new()
.set_ack(ack)
.add_attribute("method", "cross_chain_query")
.add_attribute("query_type", query_type))
}
fn handle_cross_chain_transfer(
_deps: DepsMut,
_env: Env,
_packet: cosmwasm_std::IbcPacket,
recipient: String,
amount: cosmwasm_std::Uint128,
denom: String,
) -> Result<IbcReceiveResponse, ContractError> {
let ack = AcknowledgementMsg::Ok(Binary::from(
to_binary(&CrossChainResponse {
query_type: "transfer".to_string(),
data: Binary::default(),
success: true,
})
.unwrap()
.as_slice(),
));
Ok(IbcReceiveResponse::new()
.set_ack(ack)
.add_attribute("method", "cross_chain_transfer")
.add_attribute("recipient", recipient)
.add_attribute("amount", amount.to_string()))
}
fn handle_state_update(
_deps: DepsMut,
_env: Env,
_packet: cosmwasm_std::IbcPacket,
key: String,
value: Binary,
) -> Result<IbcReceiveResponse, ContractError> {
let ack = AcknowledgementMsg::Ok(Binary::from(
to_binary(&CrossChainResponse {
query_type: "state_update".to_string(),
data: value.clone(),
success: true,
})
.unwrap()
.as_slice(),
));
Ok(IbcReceiveResponse::new()
.set_ack(ack)
.add_attribute("method", "state_update")
.add_attribute("key", key))
}
/// 处理 IBC 确认
#[entry_point]
pub fn ibc_packet_ack(
deps: DepsMut,
_env: Env,
msg: IbcPacketAckMsg,
) -> StdResult<IbcBasicResponse> {
let ack: AcknowledgementMsg = from_binary(&msg.acknowledgement.data)?;
let mut response = IbcBasicResponse::new()
.add_attribute("method", "ibc_packet_ack")
.add_attribute("sequence", msg.original_packet.sequence.to_string());
match ack {
AcknowledgementMsg::Ok(data) => {
let response_data: CrossChainResponse = from_binary(&data)?;
response = response
.add_attribute("ack_status", "success")
.add_attribute("query_type", response_data.query_type);
}
AcknowledgementMsg::Error(err) => {
response = response
.add_attribute("ack_status", "error")
.add_attribute("error", err);
}
}
Ok(response)
}
/// 处理 IBC 超时
#[entry_point]
pub fn ibc_packet_timeout(
_deps: DepsMut,
_env: Env,
msg: IbcPacketTimeoutMsg,
) -> StdResult<IbcBasicResponse> {
// 超时处理:可以触发退款逻辑
Ok(IbcBasicResponse::new()
.add_attribute("method", "ibc_packet_timeout")
.add_attribute("sequence", msg.packet.sequence.to_string()))
}
/// 自定义 Never 类型(用于 IBC Receive 永不失败)
pub enum Never {}
impl From<Never> for StdError {
fn from(_: Never) -> Self {
unreachable!()
}
}
6.1.3 src/msg.rs — IBC 消息类型
use cosmwasm_schema::cw_serde;
use cosmwasm_std::{Binary, Uint128};
/// 合约执行消息 (非 IBC)
#[cw_serde]
pub enum ExecuteMsg {
SendIBCPacket {
channel_id: String,
msg: IbcExecuteMsg,
timeout_seconds: Option<u64>,
},
RegisterIBCChannel {
channel_id: String,
},
}
/// 合约查询消息
#[cw_serde]
pub enum QueryMsg {
GetChannelState { channel_id: String },
ListChannels {},
}
/// IBC 数据包内容
#[cw_serde]
pub enum IbcExecuteMsg {
CrossChainQuery {
query_type: String,
params: Binary,
},
CrossChainTransfer {
recipient: String,
amount: Uint128,
denom: String,
},
UpdateState {
key: String,
value: Binary,
},
}
/// IBC 确认消息
#[cw_serde]
pub enum AcknowledgementMsg {
Ok(Binary),
Error(String),
}
/// 跨链查询响应
#[cw_serde]
pub struct CrossChainResponse {
pub query_type: String,
pub data: Binary,
pub success: bool,
}
6.1.4 src/state.rs
use cosmwasm_schema::cw_serde;
use cw_storage_plus::{Item, Map};
use cosmwasm_std::IbcEndpoint;
#[cw_serde]
pub struct ChannelState {
pub endpoint: IbcEndpoint,
pub counterparty_endpoint: IbcEndpoint,
pub ordering: cosmwasm_std::IbcOrder,
pub version: String,
pub active: bool,
}
#[cw_serde]
pub struct Config {
pub admin: String,
}
pub const CONFIG: Item<Config> = Item::new("config");
pub const CHANNEL_STATE: Map<&str, ChannelState> = Map::new("channel_state");
6.1.5 src/contract.rs — 执行函数
use cosmwasm_std::{
entry_point, to_binary, Binary, Deps, DepsMut, Env, IbcMsg,
IbcTimeout, IbcTimeoutBlock, MessageInfo, Response, StdResult,
};
use crate::error::ContractError;
use crate::msg::{ExecuteMsg, IbcExecuteMsg, QueryMsg};
use crate::state::{CONFIG, Config};
#[entry_point]
pub fn instantiate(
deps: DepsMut,
_env: Env,
_info: MessageInfo,
msg: crate::msg::InstantiateMsg,
) -> StdResult<Response> {
let config = Config { admin: msg.admin };
CONFIG.save(deps.storage, &config)?;
Ok(Response::new().add_attribute("method", "instantiate"))
}
#[entry_point]
pub fn execute(
deps: DepsMut,
env: Env,
info: MessageInfo,
msg: ExecuteMsg,
) -> Result<Response, ContractError> {
match msg {
ExecuteMsg::SendIBCPacket {
channel_id,
msg: ibc_msg,
timeout_seconds,
} => execute_send_ibc_packet(deps, env, info, channel_id, ibc_msg, timeout_seconds),
ExecuteMsg::RegisterIBCChannel { channel_id } => {
execute_register_ibc_channel(deps, info, channel_id)
}
}
}
fn execute_send_ibc_packet(
_deps: DepsMut,
env: Env,
info: MessageInfo,
channel_id: String,
ibc_msg: IbcExecuteMsg,
timeout_seconds: Option<u64>,
) -> Result<Response, ContractError> {
let timeout = if let Some(secs) = timeout_seconds {
IbcTimeout::with_timestamp(env.block.time.plus_seconds(secs))
} else {
IbcTimeout::with_block(IbcTimeoutBlock {
revision: 0,
height: env.block.height + 5000,
})
};
let packet = IbcMsg::SendPacket {
channel_id: channel_id.clone(),
data: to_binary(&ibc_msg)?,
timeout,
};
Ok(Response::new()
.add_message(packet)
.add_attribute("method", "send_ibc_packet")
.add_attribute("sender", info.sender.to_string())
.add_attribute("channel_id", channel_id))
}
fn execute_register_ibc_channel(
_deps: DepsMut,
_info: MessageInfo,
channel_id: String,
) -> Result<Response, ContractError> {
Ok(Response::new()
.add_attribute("method", "register_ibc_channel")
.add_attribute("channel_id", channel_id))
}
#[entry_point]
pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
match msg {
QueryMsg::GetChannelState { channel_id } => {
let state = crate::state::CHANNEL_STATE.load(deps.storage, &channel_id)?;
to_binary(&state)
}
QueryMsg::ListChannels {} => {
let channels: Vec<String> = crate::state::CHANNEL_STATE
.range(deps.storage, None, None, cosmwasm_std::Order::Ascending)
.map(|r| r.map(|(k, _)| k))
.collect::<StdResult<_>>()?;
to_binary(&channels)
}
}
}
6.2 IBC Fee 中间件集成
IBC Fee 中间件 (ICS-29) 允许 Relayer 收取数据包中继费用。
// X-MSG-Stub=true: IBC Fee 中间件配置
/// IBC Fee 数据包前向费用
#[cw_serde]
pub struct IBCFee {
/// 转发数据包的费用
pub recv_fee: Vec<Coin>,
/// 回传确认的费用
pub ack_fee: Vec<Coin>,
/// 超时处理的费用
pub timeout_fee: Vec<Coin>,
}
/// 创建携带费用的 IBC 转账
pub fn create_fee_enabled_ibc_transfer(
channel_id: String,
to_address: String,
amount: Coin,
fee: IBCFee,
) -> Vec<CosmosMsg> {
// 1. 支付 IBC Fee
let pay_fee_msg = MsgPayPacketFee {
signer: ..., // 发送者
fee: IbcFee {
recv_fee: fee.recv_fee,
ack_fee: fee.ack_fee,
timeout_fee: fee.timeout_fee,
},
source_port_id: "transfer".to_string(),
source_channel_id: channel_id.clone(),
};
// 2. 发起 IBC 转账
let transfer_msg = IbcMsg::Transfer {
channel_id,
to_address,
amount,
timeout: ...,
};
vec![
CosmosMsg::Stargate {
type_url: "/ibc.applications.fee.v1.MsgPayPacketFee".to_string(),
value: to_binary(&pay_fee_msg).unwrap(),
},
CosmosMsg::Ibc(transfer_msg),
]
}
7. 自定义桥接合约
X-MSG-Stub=true: 本节为规划中的自定义桥接方案,用于 MSG Chain 与非 IBC 链(如 Ethereum)的交互。
7.1 Lock-Mint 桥模式
Lock-Mint 桥是最经典的资产跨链模式:在源链锁定资产,在目标链铸造等价代币。
X-MSG-Stub=true
Ethereum MSG Chain
│ │
│ 1. 用户锁定 ETH 到桥合约 │
│ ┌────────────────────┐ │
│ │ Bridge Contract │ │
│ │ (Lock 10 ETH) │ │
│ └────────────────────┘ │
│ │ │
│ │ (验证者共识) │
│ └─────────────────────────► │
│ │
│ ┌────────┐ │
│ │ Mint │ │
│ │ 10 msgETH│ │
│ └────────┘ │
│ │
│ 2. 用户销毁 msgETH 赎回 ETH │
│ ◄────────────────────────────────── │
│ │
│ ┌────────────────────┐ │
│ │ 解锁 10 ETH 给用户 │ │
│ └────────────────────┘ │
7.2 Burn-Mint 桥模式
Burn-Mint 桥适用于有原生代币映射的场景。
X-MSG-Stub=true
MSG Chain Osmosis
│ │
│ 1. 销毁原生 MSG 代币 │
│ ┌──────────────────┐ │
│ │ Burn 100 MSG │ │
│ └──────────────────┘ │
│ │ │
│ │ (跨链消息) │
│ └─────────────────────────► │
│ │
│ ┌────────┐ │
│ │ Mint │ │
│ │ 100 osmoMSG│
│ └────────┘ │
7.3 完整 CosmWasm 桥接合约
7.3.1 Cargo.toml
[package]
name = "msg-bridge-contract"
version = "1.0.0"
edition = "2021"
description = "MSG Chain Custom Bridge Contract"
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
cosmwasm-std = "1.5"
cw-storage-plus = "1.2"
cw2 = "1.1"
cw-utils = "1.0"
schemars = "0.8"
serde = { version = "1.0", features = ["derive"] }
thiserror = "1.0"
uint = "0.9"
sha2 = "0.10"
hex = "0.4"
[dev-dependencies]
cosmwasm-vm = "1.5"
cw-multi-test = "0.18"
7.3.2 src/msg.rs
use cosmwasm_schema::cw_serde;
use cosmwasm_std::{Binary, Uint128};
#[cw_serde]
pub struct InstantiateMsg {
pub admin: String,
pub validators: Vec<BridgeValidator>,
pub required_signatures: u32,
pub chain_name: String,
pub counterparty_chain_id: String,
}
#[cw_serde]
pub struct BridgeValidator {
pub address: String,
pub power: u64,
pub dilithium5_public_key: String,
}
#[cw_serde]
pub enum ExecuteMsg {
/// 锁定资产发起桥接
LockAssets {
recipient: String,
amount: Uint128,
denom: String,
target_chain: String,
},
/// 铸造资产(验证者签名后)
MintAssets {
sender: String,
recipient: String,
amount: Uint128,
denom: String,
source_chain: String,
source_tx_hash: String,
signatures: Vec<Signature>,
},
/// 燃烧资产发起赎回
BurnAssets {
amount: Uint128,
denom: String,
recipient_on_source: String,
},
/// 解锁资产(验证者签名后)
UnlockAssets {
sender: String,
recipient: String,
amount: Uint128,
denom: String,
source_chain: String,
source_tx_hash: String,
burn_tx_hash: String,
signatures: Vec<Signature>,
},
/// 更新验证者集
UpdateValidatorSet {
validators: Vec<BridgeValidator>,
required_signatures: u32,
signatures: Vec<Signature>,
},
/// 暂停桥
Pause {},
/// 恢复桥
Resume {},
}
#[cw_serde]
pub struct Signature {
pub validator_address: String,
pub signature: Binary,
}
#[cw_serde]
pub enum QueryMsg {
GetBridgeConfig {},
GetValidatorSet {},
GetLockedBalance { denom: String },
GetBridgeStatus {},
VerifyMintRequest {
sender: String,
recipient: String,
amount: Uint128,
denom: String,
source_tx_hash: String,
signatures: Vec<Signature>,
},
}
#[cw_serde]
pub struct BridgeConfig {
pub admin: String,
pub chain_name: String,
pub counterparty_chain_id: String,
pub required_signatures: u32,
pub paused: bool,
}
#[cw_serde]
pub struct BridgeTransferRequest {
pub id: String,
pub sender: String,
pub recipient: String,
pub amount: Uint128,
pub denom: String,
pub source_chain: String,
pub target_chain: String,
pub status: TransferStatus,
pub source_tx_hash: String,
pub created_at: u64,
}
#[cw_serde]
pub enum TransferStatus {
Pending,
Confirmed,
Failed,
}
7.3.3 src/state.rs
use cosmwasm_schema::cw_serde;
use cw_storage_plus::{Item, Map};
use cosmwasm_std::Uint128;
use crate::msg::{BridgeConfig, BridgeValidator, BridgeTransferRequest};
pub const CONFIG: Item<BridgeConfig> = Item::new("bridge_config");
pub const VALIDATORS: Map<&str, BridgeValidator> = Map::new("validators");
pub const LOCKED_BALANCES: Map<&str, Uint128> = Map::new("locked_balances");
pub const MINTED_BALANCES: Map<&str, Uint128> = Map::new("minted_balances");
pub const TRANSFER_REQUESTS: Map<&str, BridgeTransferRequest> = Map::new("transfer_requests");
pub const BRIDGE_NONCE: Item<u64> = Item::new("bridge_nonce");
7.3.4 src/contract.rs
use cosmwasm_std::{
entry_point, to_binary, BankMsg, Binary, Coin, Deps, DepsMut,
Env, MessageInfo, Order, Response, StdError, StdResult, Uint128,
};
use cw2::set_contract_version;
use sha2::{Digest, Sha256};
use crate::error::ContractError;
use crate::msg::{
BridgeConfig, BridgeTransferRequest, BridgeValidator, ExecuteMsg,
InstantiateMsg, QueryMsg, Signature, TransferStatus,
};
use crate::state::{
CONFIG, LOCKED_BALANCES, MINTED_BALANCES, TRANSFER_REQUESTS,
VALIDATORS, BRIDGE_NONCE,
};
const CONTRACT_NAME: &str = "msg-bridge-contract";
const CONTRACT_VERSION: &str = "1.0.0";
#[entry_point]
pub fn instantiate(
deps: DepsMut,
_env: Env,
info: MessageInfo,
msg: InstantiateMsg,
) -> StdResult<Response> {
set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
let config = BridgeConfig {
admin: msg.admin,
chain_name: msg.chain_name,
counterparty_chain_id: msg.counterparty_chain_id,
required_signatures: msg.required_signatures,
paused: false,
};
CONFIG.save(deps.storage, &config)?;
for validator in msg.validators {
VALIDATORS.save(deps.storage, &validator.address, &validator)?;
}
BRIDGE_NONCE.save(deps.storage, &0)?;
Ok(Response::new()
.add_attribute("method", "instantiate")
.add_attribute("admin", info.sender.to_string()))
}
#[entry_point]
pub fn execute(
deps: DepsMut,
env: Env,
info: MessageInfo,
msg: ExecuteMsg,
) -> Result<Response, ContractError> {
match msg {
ExecuteMsg::LockAssets {
recipient,
amount,
denom,
target_chain,
} => execute_lock_assets(deps, env, info, recipient, amount, denom, target_chain),
ExecuteMsg::MintAssets {
sender,
recipient,
amount,
denom,
source_chain,
source_tx_hash,
signatures,
} => execute_mint_assets(
deps, env, info, sender, recipient, amount, denom,
source_chain, source_tx_hash, signatures,
),
ExecuteMsg::BurnAssets {
amount,
denom,
recipient_on_source,
} => execute_burn_assets(deps, env, info, amount, denom, recipient_on_source),
ExecuteMsg::UnlockAssets {
sender,
recipient,
amount,
denom,
source_chain,
source_tx_hash,
burn_tx_hash,
signatures,
} => execute_unlock_assets(
deps, env, info, sender, recipient, amount, denom,
source_chain, source_tx_hash, burn_tx_hash, signatures,
),
ExecuteMsg::UpdateValidatorSet {
validators,
required_signatures,
signatures,
} => execute_update_validator_set(
deps, env, info, validators, required_signatures, signatures,
),
ExecuteMsg::Pause {} => execute_pause(deps, info),
ExecuteMsg::Resume {} => execute_resume(deps, info),
}
}
fn assert_not_paused(deps: &DepsMut) -> Result<(), ContractError> {
let config = CONFIG.load(deps.storage)?;
if config.paused {
return Err(ContractError::Std(StdError::generic_err("Bridge is paused")));
}
Ok(())
}
fn assert_admin(deps: &DepsMut, info: &MessageInfo) -> Result<(), ContractError> {
let config = CONFIG.load(deps.storage)?;
if info.sender.to_string() != config.admin {
return Err(ContractError::Unauthorized {});
}
Ok(())
}
fn verify_signatures(
deps: &DepsMut,
message_hash: &[u8],
signatures: &[Signature],
) -> Result<(), ContractError> {
let config = CONFIG.load(deps.storage)?;
if signatures.len() < config.required_signatures as usize {
return Err(ContractError::Std(StdError::generic_err(
"Insufficient signatures",
)));
}
let mut valid_count = 0;
let mut seen_addresses = std::collections::HashSet::new();
for sig in signatures {
if seen_addresses.contains(&sig.validator_address) {
continue; // 跳过重复签名
}
seen_addresses.insert(sig.validator_address.clone());
// 验证签名者是否为合法验证者
if let Ok(validator) = VALIDATORS.load(deps.storage, &sig.validator_address) {
// 使用 Dilithium-5 验证签名
// TODO: 接入 Dilithium-5 验证预编译
// let valid = verify_dilithium5(
// &validator.dilithium5_public_key,
// message_hash,
// &sig.signature,
// );
// if valid {
// valid_count += 1;
// }
valid_count += 1; // 占位逻辑
}
}
if valid_count < config.required_signatures as usize {
return Err(ContractError::Std(StdError::generic_err(
"Signature verification failed",
)));
}
Ok(())
}
fn compute_transfer_hash(
sender: &str,
recipient: &str,
amount: Uint128,
denom: &str,
source_tx_hash: &str,
nonce: u64,
) -> [u8; 32] {
let mut hasher = Sha256::new();
hasher.update(sender.as_bytes());
hasher.update(b":");
hasher.update(recipient.as_bytes());
hasher.update(b":");
hasher.update(amount.to_string().as_bytes());
hasher.update(b":");
hasher.update(denom.as_bytes());
hasher.update(b":");
hasher.update(source_tx_hash.as_bytes());
hasher.update(b":");
hasher.update(nonce.to_be_bytes());
hasher.finalize().into()
}
fn execute_lock_assets(
deps: DepsMut,
env: Env,
info: MessageInfo,
recipient: String,
amount: Uint128,
denom: String,
target_chain: String,
) -> Result<Response, ContractError> {
assert_not_paused(&deps)?;
// 验证用户发送了足够的资金
let sent = cw_utils::must_pay(&info, &denom)
.map_err(|_| ContractError::Std(StdError::generic_err("Insufficient payment")))?;
if sent != amount {
return Err(ContractError::Std(StdError::generic_err(
"Sent amount doesn't match",
)));
}
let mut nonce = BRIDGE_NONCE.load(deps.storage)?;
nonce += 1;
BRIDGE_NONCE.save(deps.storage, &nonce)?;
// 记录锁定量
let locked = LOCKED_BALANCES
.load(deps.storage, &denom)
.unwrap_or_default();
LOCKED_BALANCES.save(deps.storage, &denom, &(locked + amount))?;
// 创建转账请求
let request_id = format!("{}-{}", env.block.height, nonce);
let request = BridgeTransferRequest {
id: request_id.clone(),
sender: info.sender.to_string(),
recipient: recipient.clone(),
amount,
denom: denom.clone(),
source_chain: "msg-chain".to_string(),
target_chain: target_chain.clone(),
status: TransferStatus::Pending,
source_tx_hash: String::new(),
created_at: env.block.time.seconds(),
};
TRANSFER_REQUESTS.save(deps.storage, &request_id, &request)?;
Ok(Response::new()
.add_attribute("method", "lock_assets")
.add_attribute("request_id", &request_id)
.add_attribute("sender", info.sender.to_string())
.add_attribute("recipient", &recipient)
.add_attribute("amount", amount.to_string())
.add_attribute("denom", &denom)
.add_attribute("target_chain", &target_chain))
}
fn execute_mint_assets(
deps: DepsMut,
env: Env,
info: MessageInfo,
sender: String,
recipient: String,
amount: Uint128,
denom: String,
source_chain: String,
source_tx_hash: String,
signatures: Vec<Signature>,
) -> Result<Response, ContractError> {
assert_not_paused(&deps)?;
let nonce = BRIDGE_NONCE.load(deps.storage)?;
// 计算并验证签名
let msg_hash = compute_transfer_hash(
&sender,
&recipient,
amount,
&denom,
&source_tx_hash,
nonce,
);
verify_signatures(&deps, &msg_hash, &signatures)?;
// 铸造资产
let minted = MINTED_BALANCES
.load(deps.storage, &denom)
.unwrap_or_default();
MINTED_BALANCES.save(deps.storage, &denom, &(minted + amount))?;
// 发送资产给接收者
let transfer = BankMsg::Send {
to_address: recipient.clone(),
amount: vec![Coin {
denom: format!("bridge.{}", denom),
amount,
}],
};
Ok(Response::new()
.add_message(transfer)
.add_attribute("method", "mint_assets")
.add_attribute("recipient", &recipient)
.add_attribute("amount", amount.to_string())
.add_attribute("denom", &denom)
.add_attribute("source_chain", &source_chain))
}
fn execute_burn_assets(
deps: DepsMut,
env: Env,
info: MessageInfo,
amount: Uint128,
denom: String,
recipient_on_source: String,
) -> Result<Response, ContractError> {
assert_not_paused(&deps)?;
let bridge_denom = format!("bridge.{}", denom);
// 验证用户有足够的桥接资产
let balance = deps.querier.query_balance(
info.sender.to_string().as_str(),
&bridge_denom,
)?;
if balance.amount < amount {
return Err(ContractError::InsufficientBalance {});
}
// 减少铸造量
let minted = MINTED_BALANCES
.load(deps.storage, &denom)
.unwrap_or_default();
if minted < amount {
return Err(ContractError::InsufficientBalance {});
}
MINTED_BALANCES.save(deps.storage, &denom, &(minted - amount))?;
// 销毁资产 (通过发送给黑洞地址)
let burn = BankMsg::Send {
to_address: "msg1burnburnburnburnburnburnburnburnburn5uq7q7".to_string(),
amount: vec![Coin {
denom: bridge_denom,
amount,
}],
};
let nonce = BRIDGE_NONCE.load(deps.storage)?;
BRIDGE_NONCE.save(deps.storage, &(nonce + 1))?;
Ok(Response::new()
.add_message(burn)
.add_attribute("method", "burn_assets")
.add_attribute("sender", info.sender.to_string())
.add_attribute("amount", amount.to_string())
.add_attribute("denom", &denom)
.add_attribute("recipient_on_source", &recipient_on_source))
}
fn execute_unlock_assets(
deps: DepsMut,
_env: Env,
_info: MessageInfo,
sender: String,
recipient: String,
amount: Uint128,
denom: String,
source_chain: String,
source_tx_hash: String,
burn_tx_hash: String,
signatures: Vec<Signature>,
) -> Result<Response, ContractError> {
assert_not_paused(&deps)?;
// 计算解锁消息哈希(包含 burn_tx_hash)
let combined_hash = format!("{}:{}", source_tx_hash, burn_tx_hash);
let msg_hash = Sha256::digest(combined_hash.as_bytes());
verify_signatures(&deps, &msg_hash, &signatures)?;
// 减少锁定量
let locked = LOCKED_BALANCES
.load(deps.storage, &denom)
.unwrap_or_default();
if locked < amount {
return Err(ContractError::InsufficientBalance {});
}
LOCKED_BALANCES.save(deps.storage, &denom, &(locked - amount))?;
// 解锁资产给用户
let transfer = BankMsg::Send {
to_address: recipient.clone(),
amount: vec![Coin {
denom: denom.clone(),
amount,
}],
};
Ok(Response::new()
.add_message(transfer)
.add_attribute("method", "unlock_assets")
.add_attribute("recipient", &recipient)
.add_attribute("amount", amount.to_string())
.add_attribute("denom", &denom)
.add_attribute("source_chain", &source_chain))
}
fn execute_update_validator_set(
deps: DepsMut,
_env: Env,
_info: MessageInfo,
validators: Vec<BridgeValidator>,
required_signatures: u32,
_signatures: Vec<Signature>,
) -> Result<Response, ContractError> {
let mut config = CONFIG.load(deps.storage)?;
config.required_signatures = required_signatures;
CONFIG.save(deps.storage, &config)?;
// 清除旧验证者
let old_validators: Vec<String> = VALIDATORS
.range(deps.storage, None, None, Order::Ascending)
.map(|r| r.map(|(k, _)| k))
.collect::<StdResult<Vec<_>>>()?;
for addr in old_validators {
VALIDATORS.remove(deps.storage, &addr);
}
// 保存新验证者
for v in validators {
VALIDATORS.save(deps.storage, &v.address, &v)?;
}
Ok(Response::new()
.add_attribute("method", "update_validator_set")
.add_attribute("count", VALIDATORS
.range(deps.storage, None, None, Order::Ascending)
.count()
.to_string()))
}
fn execute_pause(
deps: DepsMut,
info: MessageInfo,
) -> Result<Response, ContractError> {
assert_admin(&deps, &info)?;
let mut config = CONFIG.load(deps.storage)?;
config.paused = true;
CONFIG.save(deps.storage, &config)?;
Ok(Response::new().add_attribute("method", "pause"))
}
fn execute_resume(
deps: DepsMut,
info: MessageInfo,
) -> Result<Response, ContractError> {
assert_admin(&deps, &info)?;
let mut config = CONFIG.load(deps.storage)?;
config.paused = false;
CONFIG.save(deps.storage, &config)?;
Ok(Response::new().add_attribute("method", "resume"))
}
#[entry_point]
pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
match msg {
QueryMsg::GetBridgeConfig {} => to_binary(&CONFIG.load(deps.storage)?),
QueryMsg::GetValidatorSet {} => {
let validators: Vec<BridgeValidator> = VALIDATORS
.range(deps.storage, None, None, Order::Ascending)
.map(|r| r.map(|(_, v)| v))
.collect::<StdResult<Vec<_>>>()?;
to_binary(&validators)
}
QueryMsg::GetLockedBalance { denom } => {
let balance = LOCKED_BALANCES
.load(deps.storage, &denom)
.unwrap_or_default();
to_binary(&balance)
}
QueryMsg::GetBridgeStatus {} => {
let config = CONFIG.load(deps.storage)?;
let validator_count = VALIDATORS
.range(deps.storage, None, None, Order::Ascending)
.count();
to_binary(&serde_json::json!({
"paused": config.paused,
"validator_count": validator_count,
"required_signatures": config.required_signatures,
}))
}
QueryMsg::VerifyMintRequest {
sender,
recipient,
amount,
denom,
source_tx_hash,
signatures,
} => {
let nonce = BRIDGE_NONCE.load(deps.storage).unwrap_or(0);
let msg_hash = compute_transfer_hash(
&sender, &recipient, amount, &denom, &source_tx_hash, nonce,
);
let mut valid_count = 0;
for sig in &signatures {
if let Ok(validator) = VALIDATORS.load(deps.storage, &sig.validator_address) {
let _ = validator; // TODO: 实际验证签名
valid_count += 1;
}
}
let config = CONFIG.load(deps.storage)?;
to_binary(&serde_json::json!({
"valid": valid_count >= config.required_signatures as usize,
"signatures_required": config.required_signatures,
"signatures_provided": valid_count,
}))
}
}
}
7.3.5 src/error.rs
use cosmwasm_std::{StdError, Uint128};
use thiserror::Error;
#[derive(Error, Debug, PartialEq)]
pub enum ContractError {
#[error("{0}")]
Std(#[from] StdError),
#[error("Unauthorized")]
Unauthorized {},
#[error("Insufficient balance: required {required}, available {available}")]
InsufficientBalance {},
#[error("Insufficient signatures: required {required}, provided {provided}")]
InsufficientSignatures { required: u32, provided: usize },
#[error("Invalid signature from {validator}")]
InvalidSignature { validator: String },
#[error("Transfer {request_id} not found")]
TransferNotFound { request_id: String },
#[error("Transfer {request_id} already completed")]
TransferAlreadyCompleted { request_id: String },
}
8. 跨链dApp示例
X-MSG-Stub=true: 本节所有 dApp 为概念设计,尚未实现。
8.1 跨链 AI Agent 市场
概念: AI Agent 在 MSG Chain 上注册,但可以跨链被 Osmosis 上的用户发现和支付。
8.1.1 架构设计
X-MSG-Stub=true
跨链 AI Agent 市场
┌─────────────────────────────────────────────────────────┐
│ │
│ MSG Chain (Agent 注册链) Osmosis (发现/支付链) │
│ │
│ ┌───────────────────┐ ┌───────────────────────┐ │
│ │ agent_registry_v1 │◄────►│ Agent Market App │ │
│ │ (Agent 注册) │ IBC │ (市场前端) │ │
│ └───────────────────┘ └───────────────────────┘ │
│ │
│ ┌───────────────────┐ ┌───────────────────────┐ │
│ │ agent_payment_v1 │◄────►│ Cross-chain Payment │ │
│ │ (AIPAY 支付) │ IBC │ (跨链支付结算) │ │
│ └───────────────────┘ └───────────────────────┘ │
│ │
│ ┌───────────────────┐ ┌───────────────────────┐ │
│ │ aidid_did_v1 │◄────►│ DID Resolver │ │
│ │ (DID 身份) │ IBC │ (身份解析) │ │
│ └───────────────────┘ └───────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────┘
8.1.2 合约端 (MSG Chain) — Agent 注册
Agent 在 MSG Chain 上注册时,可以指定跨链可发现性:
// X-MSG-Stub=true: Agent 跨链注册消息 (沿用 agent_registry_v1 模式)
pub struct RegisterCrossChainAgent {
pub agent_id: String,
pub did_id: String,
pub owner: String,
pub name: String,
pub description: Option<String>,
pub endpoint: Option<String>,
pub agent_type: String,
pub capabilities: Vec<String>,
/// 启用跨链发现的链列表
pub cross_chain_discovery: Vec<String>,
/// 接受的跨链支付代币
pub accepted_tokens: Vec<CrossChainToken>,
}
pub struct CrossChainToken {
pub chain_id: String,
pub denom: String,
pub ibc_channel: String,
pub min_price: Uint128,
}
pub struct CrossChainAgentListing {
pub agent_id: String,
pub name: String,
pub description: String,
pub capabilities: Vec<String>,
pub accepted_tokens: Vec<CrossChainToken>,
pub owner: String,
pub rating: u32,
}
8.1.3 前端查询 (Osmosis 侧)
// X-MSG-Stub=true: 跨链 Agent 查询 (从 Osmosis 查询 MSG Chain 的 Agent)
const MSG_CHAIN_RPC = "https://rpc.msgchain.org:26657";
const AGENT_REGISTRY_CONTRACT = "msg1agentregistrycontractaddress...";
interface CrossChainAgent {
agent_id: string;
name: string;
description: string;
capabilities: string[];
accepted_tokens: CrossChainToken[];
owner: string;
rating: number;
}
interface CrossChainToken {
chain_id: string;
denom: string;
ibc_channel: string;
min_price: string;
}
/**
* 通过 IBC 查询跨链 Agent 列表
* 使用 IBC 数据包进行跨链查询
*/
async function queryCrossChainAgents(
ibcChannel: string
): Promise<CrossChainAgent[]> {
// 1. 构建 IBC 查询数据包
const queryPacket = {
cross_chain_query: {
query_type: "list_agents",
params: {
limit: 100,
include_cross_chain: true,
},
},
};
// 2. 通过 CosmJS 发送查询
// 注意: 这需要一个支持 IBC 查询的合约
// 实际实现需要通过一个查询代理合约
// 模拟返回数据
return [
{
agent_id: "agent-llm-001",
name: "跨链 LLM 助手",
description: "基于 GPT-4 的多语言 AI 助手",
capabilities: ["text-generation", "code-review", "translation"],
accepted_tokens: [
{
chain_id: "osmosis-1",
denom: "uosmo",
ibc_channel: "channel-1",
min_price: "1000000",
},
{
chain_id: "cosmoshub-4",
denom: "uatom",
ibc_channel: "channel-2",
min_price: "500000",
},
],
owner: "msg1agentowner...",
rating: 4,
},
];
}
/**
* 通过 IBC 发起跨链支付
*/
async function crossChainPayAgent(
agentId: string,
token: CrossChainToken,
amount: string,
userAddress: string
): Promise<string> {
const signingClient = await SigningStargateClient.connectWithSigner(
"https://rpc.osmosis.zone:26657",
wallet
);
// 构建 IBC 转账给 Agent 支付合约
const transferMsg = {
typeUrl: "/ibc.applications.transfer.v1.MsgTransfer",
value: {
sourcePort: "transfer",
sourceChannel: token.ibc_channel,
token: { denom: token.denom, amount },
sender: userAddress,
receiver: AGENT_REGISTRY_CONTRACT,
timeoutHeight: { revisionNumber: 0, revisionHeight: 500000 },
timeoutTimestamp: 0n,
memo: JSON.stringify({
agent_id: agentId,
action: "execute_task",
task_data: { /* 任务参数 */ },
}),
},
};
const fee = { amount: [{ denom: "uosmo", amount: "5000" }], gas: "250000" };
const result = await signingClient.signAndBroadcast(
userAddress,
[transferMsg],
fee
);
return result.transactionHash;
}
8.2 跨链支付结算
利用 IBC 实现 MSG Chain 到 Osmosis 的跨链支付结算。
X-MSG-Stub=true
用户 (Osmosis) MSG Chain Agent
│ │
│ 1. 支付 100 OSMO │
│ (IBC 转账 + memo) │
│ ─────────────────────────────────► │
│ │
│ 2. Agent 接收支付 │
│ 确认任务执行 │
│ ◄───────────────────────────────── │
│ │
│ 3. 任务完成,确认结算 │
│ ─────────────────────────────────► │
│ │
│ 4. Agent 通过 IBC 转回 │
│ 应得的 MSG 代币 │
│ ◄───────────────────────────────── │
// X-MSG-Stub=true: 跨链支付结算
interface CrossChainPayment {
paymentId: string;
payer: string;
payee: string;
sourceChain: string;
destinationChain: string;
amount: string;
denom: string;
status: "pending" | "confirmed" | "refunded";
memo: string;
}
async function settleCrossChainPayment(
payment: CrossChainPayment,
ibcChannel: string
): Promise<string> {
// 在来源链发起 IBC 转账附加支付信息
if (payment.sourceChain === "osmosis-1") {
const client = await SigningStargateClient.connectWithSigner(
"https://rpc.osmosis.zone:26657",
wallet
);
const result = await client.sendIbcTokens(
payment.payer,
payment.payee,
{ denom: payment.denom, amount: payment.amount },
"transfer",
ibcChannel,
{ revisionNumber: 0, revisionHeight: 500000 },
undefined,
{ amount: [{ denom: "uosmo", amount: "5000" }], gas: "200000" },
JSON.stringify({
payment_id: payment.paymentId,
type: "agent_payment",
})
);
return result.transactionHash;
}
throw new Error(`Unsupported source chain: ${payment.sourceChain}`);
}
8.3 跨链身份 (DID) 解析
利用 IBC 实现 MSG Chain 的 DID 在其他链上解析。
// X-MSG-Stub=true: 跨链 DID 解析消息
pub struct CrossChainDIDQuery {
pub did_id: String,
pub query_chain: String,
pub requester: String,
}
pub struct CrossChainDIDResponse {
pub did_document: DIDDocument,
pub verified: bool,
pub verification_chain: String,
}
// X-MSG-Stub=true: 跨链 DID 解析
const DID_REGISTRY_ADDRESS = "msg1didregistry...";
async function resolveCrossChainDID(
didId: string,
targetChain: string
): Promise<any> {
if (targetChain === "msg-chain-1") {
// 直接在 MSG Chain 上查询
const client = await SigningStargateClient.connect(MSG_CHAIN_RPC);
const result = await client.queryContractSmart(DID_REGISTRY_ADDRESS, {
resolve_did: { did_id: didId },
});
return result;
} else {
// 通过 IBC 查询
console.log(`Cross-chain DID resolution for ${didId} from ${targetChain}`);
// 实际需要通过 IBC 数据包发送查询
}
}
8.4 TypeScript 跨链交易构建器
// X-MSG-Stub=true: 跨链交易构建器
import {
SigningStargateClient,
MsgTransferEncodeObject,
calculateFee,
GasPrice,
} from "@cosmjs/stargate";
import { MsgTransfer } from "cosmjs-types/ibc/applications/transfer/v1/tx";
import { Height } from "cosmjs-types/ibc/core/client/v1/client";
interface IBCMsgTransferConfig {
sourcePort: string;
sourceChannel: string;
token: Coin;
sender: string;
receiver: string;
timeoutHeight?: Height;
timeoutTimestamp?: bigint;
memo?: string;
}
/**
* MSG Chain 跨链交易构建器
* 注意: 签名部分需适配 Dilithium-5
*/
class MSGIBCTransactionBuilder {
private client: SigningStargateClient;
private sender: string;
private gasPrice: GasPrice;
constructor(client: SigningStargateClient, sender: string) {
this.client = client;
this.sender = sender;
this.gasPrice = GasPrice.fromString("1000000000umsg");
}
/**
* 构建 IBC 转账消息
*/
buildIBCTransferMsg(config: IBCMsgTransferConfig): MsgTransferEncodeObject {
return {
typeUrl: "/ibc.applications.transfer.v1.MsgTransfer",
value: MsgTransfer.fromPartial({
sourcePort: config.sourcePort,
sourceChannel: config.sourceChannel,
token: config.token,
sender: config.sender,
receiver: config.receiver,
timeoutHeight: config.timeoutHeight ?? {
revisionNumber: 0,
revisionHeight: 50000,
},
timeoutTimestamp: config.timeoutTimestamp ?? 0n,
memo: config.memo ?? "",
}),
};
}
/**
* 批量 IBC 转账 (批量中继)
*/
async batchIBCTransfer(
transfers: IBCMsgTransferConfig[]
): Promise<string> {
const msgs = transfers.map((t) => this.buildIBCTransferMsg(t));
const fee = calculateFee(300000, this.gasPrice);
const result = await this.client.signAndBroadcast(
this.sender,
msgs,
fee,
"MSG Chain 批量跨链转账"
);
return result.transactionHash;
}
/**
* IBC 超时高度计算器
*/
calculateTimeoutHeight(
currentHeight: number,
blocksToWait: number = 10000
): Height {
return {
revisionNumber: 0,
revisionHeight: BigInt(currentHeight + blocksToWait),
};
}
}
// 使用示例
async function example() {
const builder = new MSGIBCTransactionBuilder(
await SigningStargateClient.connect("https://rpc.msgchain.org:26657"),
"msg1qypqxpq9kcrn2c9afea5lq35ef37c5x7jqylz3"
);
const txMsg = builder.buildIBCTransferMsg({
sourcePort: "transfer",
sourceChannel: "channel-0",
token: { denom: "umsg", amount: "100000000000000000000" },
sender: "msg1qypqxpq9kcrn2c9afea5lq35ef37c5x7jqylz3",
receiver: "cosmos1recipient...",
memo: "跨链支付: AI Agent 服务费",
});
console.log("IBC 转账消息已构建:", txMsg);
}
9. 安全考量
9.1 IBC 安全假设
| 威胁模型 | 描述 | MSG Chain 缓解措施 |
|---|---|---|
| 恶意验证者 | 验证者集体作恶伪造状态 | Dilithium-5 签名防伪,DAR 信誉评分 |
| 轻客户端误导 | 提交假区块头欺骗 LC | Dilithium-5 签名验证,信任周期检查 |
| Relayer DoS | Relayer 不转发数据包 | 多 Relayer 并行,IBC 超时机制 |
| 重放攻击 | 重复提交已处理的数据包 | sequence 防重放,IBC 状态跟踪 |
| 中间人攻击 | 拦截或篡改 IBC 数据包 | 数据包已签名,篡改无法通过 LC 验证 |
| 长程攻击 | 远距离分叉伪造历史 | 解绑周期保护,检查点机制 |
9.2 Dilithium-5 轻客户端安全优势
MSG Chain 使用 Dilithium-5 后量子签名,相比传统 Ed25519 IBC 客户端:
量子攻击威胁时间线:
┌─────────────────────────────────────────────────────────────┐
│ 2025 2030 2035 2040 2045 2050 │
│ │ │ │ │ │ │ │
│ ─── Ed25519 安全区间 ────────────── Shor 攻击可行性 ──► │
│ ─── Dilithium-5 安全区间 ────────────────────────────────► │
│ │
│ MSG Chain 选择 Dilithium-5 确保持续至量子时代的安全性 │
└─────────────────────────────────────────────────────────────┘
| 安全特性 | Ed25519 LC | Dilithium-5 LC (MSG) |
|---|---|---|
| 量子安全 | ❌ | ✅ (NIST 标准化) |
| 签名伪造成本 | 2^128 (经典) | 2^256 (经典) |
| 信任假设 | DPoS 安全 | DPoS + 后量子密码学 |
| 未来兼容性 | 需迁移 | 抗量子 |
9.3 桥接风险分析
9.3.1 风险矩阵
| 风险 | 可能性 | 影响 | 评分 | 缓解措施 |
|---|---|---|---|---|
| 验证者合谋伪造 | 低 | 极高 | 高风险 | 多签阈值 2/3+,旋转验证集 |
| Dilithium-5 实现漏洞 | 低 | 高 | 中风险 | 开源审计,形式化验证 |
| Relayer 审查 | 中 | 中 | 中风险 | 多 Relayer 竞争,费用激励 |
| IBC 数据包超时 | 低 | 低 | 低风险 | 自动退款机制 |
| 智能合约漏洞 | 中 | 高 | 高风险 | 多重审计,Bug Bounty |
| 桥接代币通胀 | 低 | 极高 | 高风险 | 锁定/铸造核对,链上证明 |
9.3.2 安全建议
X-MSG-Stub=true
┌────────────────────────────────────────────────────────────┐
│ MSG Chain 跨链安全最佳实践 │
│ │
│ 1. 验证者集管理 │
│ ├── 至少 7 个桥验证者 │
│ ├── 2/3+ 多签阈值 │
│ ├── 定期轮换 (每 1000 区块) │
│ └── DAR 评分过滤低信誉验证者 │
│ │
│ 2. 流动性管理 │
│ ├── 每日跨链限额 │
│ ├── 总锁定上限 │
│ ├── 自动暂停 (异常流量检测) │
│ └── 慢启动 (逐步增加限额) │
│ │
│ 3. 监控与告警 │
│ ├── 实时监控 IBC 数据包延迟 │
│ ├── 异常转账模式检测 │
│ ├── 验证者签名异常 │
│ └── 治理紧急暂停机制 │
│ │
│ 4. 审计与形式化验证 │
│ ├── Dilithium-5 LC 形式化验证 (Coq) │
│ ├── 桥接合约第三方审计 │
│ ├── Bug Bounty 计划 (最高 100 万 MSG) │
│ └── 渐进式上线 (Testnet → 小规模 → 全量) │
└────────────────────────────────────────────────────────────┘
9.4 MEV 在跨链场景中
跨链 MEV (最大可提取价值) 是跨链桥特有的安全问题:
| MEV 类型 | 描述 | MSG Chain 对策 |
|---|---|---|
| 跨链三明治攻击 | 抢先/延迟跨链交易获取利润 | 有序通道 (Ordered channel) |
| 数据包重排序 | Relayer 重新排序数据包 | 序列号强制执行 |
| 时间戳操纵 | 利用跨链时间差套利 | Dilithium-5 时钟偏差检查 |
| 审查后套利 | 延迟特定交易后套利 | 多 Relayer 并行 |
9.5 安全最佳实践清单
X-MSG-Stub=true
## 跨链部署检查清单
### 合约安全
- [ ] 桥接合约已通过专业审计(至少 2 家审计机构)
- [ ] Dilithium-5 验证实现已形式化验证
- [ ] 所有外部调用使用 reentrancy guard
- [ ] 整数溢出保护(use `Uint128` / `checked_*`)
- [ ] 暂停机制可通过治理触发
- [ ] 可升级代理使用时间锁(> 48 小时)
### Relayer 安全
- [ ] Relayer 密钥使用 HSM 或硬件钱包
- [ ] 至少有 3 个独立的 Relayer 运营商
- [ ] Relayer 节点配置了监控和告警
- [ ] Relayer 密钥定期轮换
### 运行安全
- [ ] 跨链限额已配置(每日/总量)
- [ ] 异常检测系统已部署
- [ ] 紧急暂停测试已完成
- [ ] 回滚/退款流程已测试
- [ ] Bug Bounty 未独立核验上线状态
10. 开发路线图
X-MSG-Stub=true: 以下路线图为规划中的开发路径,时间线可能根据实际情况调整。
10.1 阶段总览
X-MSG-Stub=true
Q3 2026 Q4 2026 Q1 2027 Q2 2027 Q3 2027 Q4 2027
│ │ │ │ │ │
│ Phase 1 │ Phase 2 │ Phase 3 │ Phase 4 │ Phase 5 │
│ │ │ │ │ │
│ IBC │ Relayer │ ICS-20 │ Custom │ dApp │
│ Client │ Setup │ Transfer │ Bridge │ Ecosystem│
│ │ │ │ │ │
▼ ▼ ▼ ▼ ▼ ▼
10.2 Phase 1: IBC 客户端实现 (Q3 2026)
目标: 实现 Dilithium-5 IBC 轻客户端
| 任务 | 工作量 | 依赖 |
|---|---|---|
| Dilithium-5 轻客户端规范 | 4 周 | IBC v2 规范 |
| Dilithium-5 共识状态验证 | 3 周 | Dilithium-5 签名库 |
| 轻客户端状态管理 | 2 周 | Cosmos SDK IBC |
| 误行为检测 | 3 周 | 轻客户端验证逻辑 |
| 单元测试 | 4 周 | 所有前置任务 |
| 集成测试 | 2 周 | 单元测试 |
主要代码: x/ibc-lightclient/dilithium5/
x/ibc-lightclient/dilithium5/
├── client_state.go # 客户端状态
├── consensus_state.go # 共识状态
├── header.go # 区块头验证
├── misbehaviour.go # 误行为检测
├── update.go # 状态更新
├── verify.go # 验证逻辑
├── types.go # 类型定义
└── tests/
├── client_test.go
└── misbehaviour_test.go
10.3 Phase 2: Relayer 搭建与测试 (Q4 2026)
目标: 搭建测试网 Relayer,验证 IBC 连接
| 任务 | 工作量 | 依赖 |
|---|---|---|
| MSG Chain 测试网部署 | 2 周 | Phase 1 |
| Go Relayer 扩展(Dilithium-5 支持) | 4 周 | Relayer 代码库 |
| Hermes 扩展 | 4 周 | Hermes 代码库 |
| 连接/通道握手测试 | 3 周 | Relayer 就绪 |
| 数据包中继测试 | 2 周 | 连接建立 |
| 性能基准测试 | 2 周 | 中继测试 |
| 端到端测试网 | 1 周 | 以上全部 |
部署架构:
X-MSG-Stub=true
┌──────────────────────────────────────────────────┐
│ 测试网环境 │
│ │
│ ┌──────────────┐ ┌──────────────────────┐ │
│ │ MSG Chain │◄───►│ Cosmos Hub Testnet │ │
│ │ Testnet │ │ (Theta Testnet) │ │
│ └──────────────┘ └──────────────────────┘ │
│ │ │ │
│ └───────────────────────┘ │
│ │ │
│ ┌─────┴──────┐ │
│ │ Relayer │ │
│ │ (rly/her) │ │
│ └────────────┘ │
└──────────────────────────────────────────────────┘
10.4 Phase 3: ICS-20 集成 (Q1 2027)
目标: MSG Chain 支持标准 ICS-20 代币转移
| 任务 | 工作量 | 依赖 |
|---|---|---|
| ICS-20 模块适配 | 3 周 | Phase 2 |
| 托管模块实现 | 2 周 | Bank 模块 |
| IBC Denom 追踪 | 1 周 | ICS-20 模块 |
| CLI 命令实现 | 2 周 | 模块实现 |
| CosmWasm IBC 集成 | 4 周 | CosmWasm + IBC |
| Token Manager 合约 | 3 周 | CosmWasm |
| 测试网跨链转账 | 2 周 | 以上全部 |
预期交付:
# Phase 3 完成后可执行
msgd tx ibc-transfer transfer transfer channel-0 \
cosmos1recipient... 100umsg --from my-key
msgd query ibc-transfer denom-trace <hash>
msgd query bank balances msg1... --denom ibc/<hash>
10.5 Phase 4: 自定义桥接合约 (Q2-Q3 2027)
目标: 实现与非 IBC 链的桥接(如 Ethereum)
| 任务 | 工作量 | 依赖 |
|---|---|---|
| Lock-Mint 合约开发 | 4 周 | CosmWasm |
| Burn-Mint 合约开发 | 3 周 | CosmWasm |
| 桥验证者集合约 | 3 周 | Phase 3 |
| Dilithium-5 多签实现 | 4 周 | Dilithium-5 库 |
| Ethereum 桥合约 (Solidity) | 4 周 | Solidity 开发 |
| 跨链验证协调器 | 3 周 | 桥合约 |
| 安全审计 | 4 周 | 以上全部 |
桥接架构:
X-MSG-Stub=true
MSG Chain (CosmWasm) Ethereum (EVM)
│ │
│ ┌───────────────────────┐ │
│ │ MSG Bridge Contract │ │
│ │ - Lock/Burn │ │
│ │ - Validator Signatures │ │
│ │ - Mint/Unlock │ │
│ └───────────────────────┘ │
│ │ │
│ │ (验证者共识) │
│ │ │
│ └──────────────────► │
│ │
│ ┌────────┐ │
│ │ ETH │ │
│ │ Bridge │ │
│ │ Contract│ │
│ └────────┘ │
10.6 Phase 5: 跨链 dApp 生态 (Q4 2027)
目标: 建立跨链 dApp 生态
| 任务 | 工作量 | 依赖 |
|---|---|---|
| 跨链 AI Agent 市场 | 6 周 | Phase 3 |
| 跨链支付 SDK | 4 周 | Phase 3+4 |
| 跨链 DID 解析器 | 3 周 | DID 合约 |
| 跨链浏览器 | 4 周 | 索引器 |
| 开发者文档 | 3 周 | 全部 |
| Bug Bounty 上线 | 1 周 | 审计完成 |
10.7 依赖关系图
X-MSG-Stub=true
Phase 1 ──► Phase 2 ──► Phase 3 ──► Phase 5
│ ▲
│ │
▼ │
Phase 4 ──────────────────┘
依赖关系:
- Phase 2 依赖 Phase 1 (轻客户端实现)
- Phase 3 依赖 Phase 2 (Relayer)
- Phase 4 依赖 Phase 3 (IBC 基础)
- Phase 5 依赖 Phase 3 + Phase 4
10.8 团队配置建议
| 角色 | 人数 | 阶段 |
|---|---|---|
| Cosmos SDK/IBC 核心开发者 | 2-3 | Phase 1-3 |
| Dilithium-5 密码学工程师 | 1-2 | Phase 1 |
| CosmWasm 合约开发者 | 2-3 | Phase 3-5 |
| Rust 后端开发者 | 1-2 | Phase 2 |
| TypeScript/前端开发者 | 1-2 | Phase 5 |
| 安全工程师 | 1 | Phase 1-5 |
| DevOps/Relayer 工程师 | 1 | Phase 2-5 |
10.9 风险与缓解
| 风险 | 概率 | 影响 | 缓解 |
|---|---|---|---|
| Dilithium-5 IBC 规范延迟 | 中 | 高 | 并行开发,先用 Tendermint LC |
| Relayer 兼容性问题 | 中 | 中 | 自研 MSG Relayer 备选 |
| CosmWasm IBC 集成复杂度 | 低 | 高 | 先 ICS-20,后自定义逻辑 |
| 安全审计发现严重问题 | 低 | 高 | 预留 2 个月修复窗口 |
| 开发者采用率低 | 中 | 中 | 提供完整文档和样板合约 |
附录 A: 快速参考
A.1 关键命令速查
# X-MSG-Stub=true: 跨链操作速查
# IBC 客户端操作
msgd tx ibc client create [flags]
msgd query ibc client state [client-id]
msgd query ibc client list
# 连接操作
msgd tx ibc connection open-init [flags]
msgd query ibc connection end [connection-id]
msgd query ibc connections
# 通道操作
msgd tx ibc channel open-init [flags]
msgd query ibc channel end [port-id] [channel-id]
msgd query ibc channels
# 代币转移
msgd tx ibc-transfer transfer [src-port] [src-channel] [receiver] [amount]
msgd query ibc-transfer denom-trace [hash]
msgd query ibc-transfer escrow-address [port] [channel-id]
# 数据包查询
msgd query ibc channel packet-commitment [port-id] [channel-id] [sequence]
msgd query ibc channel packet-ack [port-id] [channel-id] [sequence]
msgd query ibc channel unreceived-packets [port-id] [channel-id]
msgd query ibc channel unreceived-acks [port-id] [channel-id]
# Relayer
msgd query ibc client status [client-id]
msgd query ibc connection connections
A.2 IBC Denom 计算
// X-MSG-Stub=true: IBC Denom 哈希计算
import { createHash } from "crypto";
function computeIBCDenomHash(
portId: string,
channelId: string,
baseDenom: string
): string {
const path = `${portId}/${channelId}/${baseDenom}`;
const hash = createHash("sha256")
.update(path)
.digest("hex")
.toUpperCase();
return `ibc/${hash}`;
}
// 示例
const denomHash = computeIBCDenomHash("transfer", "channel-0", "umsg");
console.log(denomHash);
// 输出: ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2
A.3 端口分配
| 端口 | 用途 | 模块 |
|---|---|---|
transfer |
ICS-20 代币转账 | ibc-transfer |
wasm.{contract_addr} |
CosmWasm 合约 IBC | wasm |
icahost |
ICS-27 跨链账户 | ica |
icacontroller |
ICS-27 控制端 | ica |
fee |
IBC Fee 中间件 | ibc-fee |
A.4 相关资源
| 资源 | 链接 |
|---|---|
| IBC 规范 | https://github.com/cosmos/ibc |
| CosmWasm IBC 文档 | https://docs.cosmwasm.com/docs/1.0/ibc/ |
| Go Relayer | https://github.com/cosmos/relayer |
| Hermes Relayer | https://hermes.informal.systems/ |
| MSG Chain 白皮书 | https://msgchain.org/whitepaper |
| Dilithium-5 标准 | https://pq-crystals.org/dilithium/ |
注意: 本文档中所有标记
X-MSG-Stub=true的内容均为规划中的功能,尚未在主网上实现。实际可用的跨链功能请参考 MSG Chain 官方文档和主网状态。本文档基于 MSG Chain 白皮书 v0.1 和 Cosmos IBC 标准编写。
