dApp Docs/安全审计清单与常见漏洞
Development reference. Not independently verified for production.

MSG Chain 安全审计清单与常见漏洞指南

数据来源:MSG Chain 代码库核实

主网状态: No-Go — 当前 MSGChain 主网裁决为 No-Go,以下内容反映代码实际状态,不代表生产可用。

适用链: MSG Chain (基于Cosmos SDK + DAR Consensus + Dilithium-5)
适用合约: CosmWasm 智能合约


目录

  1. 安全审计概述
  2. CosmWasm常见漏洞
  3. MSG Chain特有安全考量
  4. 合约安全审计检查表
  5. 安全编码规范
  6. 审计工具
  7. 审计报告模板
  8. 安全部署检查清单
  9. 事故响应

1. 安全审计概述

1.1 MSG Chain 安全模型

MSG Chain 是基于 Cosmos SDK 构建的 Layer 1 应用链,采用以下核心安全机制:

安全层 技术实现 安全等级
共识层 DAR (Dynamic Authority Rotation) 共识 中 - 高
签名层 Dilithium-5 后量子密码签名 高 (PQ安全)
存储层 BadgerDB (键值存储) 中
智能合约 CosmWasm (wasmd) 中 - 高
跨链 IBC (规划阶段) 待审计

1.2 威胁模型

┌─────────────────────────────────────────────────┐
│                   攻击者能力                       │
├─────────────────────────────────────────────────┤
│  🟢 链上交易监控 (Mempool监听)                    │
│  🟡 有限计算资源 (非量子计算机)                    │
│  🟡 控制部分验证者集 (少于104/96阈值)             │
│  🔴 无法攻破 Dilithium-5 (后量子安全)              │
│  🔴 无法伪造 DAR 共识                              │
└─────────────────────────────────────────────────┘

信任假设:

不信任假设:

1.3 后量子密码学优势

MSG Chain 采用 Dilithium-5 签名算法,相比 ECDSA/EdDSA 提供:

抗量子攻击        Dilithium-5 ✅ | ECDSA ❌ | EdDSA ❌
签名大小          4595 bytes (Dilithium-5) | 64-72 bytes (ECDSA) | 64 bytes (EdDSA)
公钥大小          2592 bytes (Dilithium-5) | 32-33 bytes (ECDSA) | 32 bytes (EdDSA)
验证速度          较慢 (3-5x ECDSA) | 快 | 快

安全影响:

1.4 错误码参考映射

审计过程中可能遇到的系统级错误码:

错误码 名称 说明 安全含义
3 UNAUTHORIZED 未授权操作 权限检查失败
4 INSUFFICIENT_FUNDS 余额不足 资金验证
5 CONTRACT_EXECUTION_FAILED 合约执行失败 通用执行错误
7 INVALID_PARAMETER 无效参数 输入验证
10 AGENT_NOT_FOUND Agent 未找到 AI Agent 不存在
12 CONSTITUTION_VIOLATION 违反 Agent 宪法 行为约束违规
15 INVALID_SIGNATURE Dilithium-5 签名无效 签名验证失败
16 DUPLICATE_NONCE 重复 Nonce 重放攻击检测
17 RATE_LIMIT_EXCEEDED 超出门限 速率限制

2. CosmWasm 常见漏洞

本章涵盖 20 种在 CosmWasm 合约中发现的典型安全漏洞。每个漏洞均包含:漏洞描述、脆弱代码示例、攻击场景、修复代码及测试用例。


VULN-01: 重入攻击 (Reentrancy)

描述

CosmWasm 的 reply 机制可能被利用进行重入攻击。与 EVM 不同,CosmWasm 默认不支持同步调用重入,但通过 SubMsg + Reply 模式,攻击者可以在 reply 处理函数中重新进入合约状态。

类似 The DAO 重入攻击(2016年,360万 ETH 被盗),攻击者利用合约在状态更新前调用了外部合约。

脆弱代码

// ⚠️ 脆弱版本 — 存在重入风险
use cosmwasm_std::{
    entry_point, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdError,
    StdResult, SubMsg, Reply, BankMsg, Coin, Uint128, CosmosMsg,
    to_binary,
};
use cw_storage_plus::Item;
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct State {
    pub balances: Uint128,
    pub locked: bool,
}

const STATE: Item<State> = Item::new("state");

#[entry_point]
pub fn execute(
    deps: DepsMut,
    env: Env,
    info: MessageInfo,
    msg: ExecuteMsg,
) -> StdResult<Response> {
    match msg {
        ExecuteMsg::Withdraw { amount } => withdraw(deps, env, info, amount),
        // ...
    }
}

pub fn withdraw(deps: DepsMut, _env: Env, info: MessageInfo, amount: Uint128) -> StdResult<Response> {
    let state = STATE.load(deps.storage)?;

    if amount > state.balances {
        return Err(StdError::generic_err("Insufficient balance"));
    }

    // ⚠️ 漏洞:在状态更新前发送资金
    let bank_msg = BankMsg::Send {
        to_address: info.sender.to_string(),
        amount: vec![Coin::new(amount.u128(), "umsg")],
    };

    // ⚠️ 使用 SubMsg 允许 reply 钩子
    let sub_msg: SubMsg = SubMsg::new(bank_msg);

    // ❌ 状态更新在消息发送之后!
    STATE.save(deps.storage, &State {
        balances: state.balances.checked_sub(amount)?,
        locked: state.locked,
    })?;

    Ok(Response::new()
        .add_submessage(sub_msg)
        .add_attribute("action", "withdraw")
        .add_attribute("amount", amount))
}

攻击场景

  1. 攻击者部署恶意合约,在 reply 处理函数中重新调用 withdraw
  2. 当受害合约发送资金后、更新状态前,reply 触发再次提取
  3. 由于状态尚未更新,state.balances 仍然包含原始余额
  4. 攻击者可重复提取直到 gas 耗尽或达到栈深度限制

修复代码

// ✅ 修复版本 — Checks-Effects-Interactions 模式
pub fn withdraw(deps: DepsMut, _env: Env, info: MessageInfo, amount: Uint128) -> StdResult<Response> {
    let state = STATE.load(deps.storage)?;

    if amount > state.balances {
        return Err(StdError::generic_err("Insufficient balance"));
    }

    // ✅ 首先更新状态 (Effects)
    let new_balance = state.balances.checked_sub(amount)?;
    STATE.save(deps.storage, &State {
        balances: new_balance,
        locked: state.locked,
    })?;

    // ✅ 然后发送资金 (Interactions)
    let bank_msg = BankMsg::Send {
        to_address: info.sender.to_string(),
        amount: vec![Coin::new(amount.u128(), "umsg")],
    };

    // ✅ 使用 ReplyOn::Never 防止额外的 reply 处理
    use cosmwasm_std::ReplyOn;
    let sub_msg = SubMsg {
        id: 0,
        msg: CosmosMsg::Bank(bank_msg),
        gas_limit: None,
        reply_on: ReplyOn::Never,
    };

    Ok(Response::new()
        .add_submessage(sub_msg)
        .add_attribute("action", "withdraw")
        .add_attribute("amount", amount))
}

正式测试用例

#[cfg(test)]
mod tests {
    use super::*;
    use cosmwasm_std::testing::{
        mock_dependencies, mock_env, mock_info,
    };
    use cosmwasm_std::{coins, from_binary, Attribute};

    #[test]
    fn test_secure_withdraw_prevents_reentrancy() {
        let mut deps = mock_dependencies();
        let env = mock_env();
        let owner = String::from("owner");

        STATE.save(deps.as_mut().storage, &State {
            balances: Uint128::new(1000),
            locked: false,
        }).unwrap();

        let info = mock_info(&owner, &coins(0, "umsg"));
        let res = withdraw(deps.as_mut(), env, info, Uint128::new(500)).unwrap();

        let state = STATE.load(deps.as_ref().storage).unwrap();
        assert_eq!(state.balances, Uint128::new(500));

        let env2 = mock_env();
        let info2 = mock_info(&owner, &coins(0, "umsg"));
        let reentry_result = withdraw(deps.as_mut(), env2, info2, Uint128::new(600));
        assert!(reentry_result.is_err());
        assert_eq!(
            reentry_result.err().unwrap().to_string(),
            StdError::generic_err("Insufficient balance").to_string()
        );
    }
}

VULN-02: 未授权访问 (Unauthorized Access)

描述

最常见的 CosmWasm 漏洞 — 缺少适当的权限检查。类似 PolyNetwork 跨链桥被黑事件(2021年,6.1亿美元损失),管理员函数缺少调用者验证。

脆弱代码

// ⚠️ 脆弱版本 — 缺少权限检查
use cosmwasm_std::{
    entry_point, DepsMut, Env, MessageInfo, Response, StdError, StdResult,
};
use cw_storage_plus::Item;

const OWNER: Item<String> = Item::new("owner");
const TOTAL_SUPPLY: Item<Uint128> = Item::new("total_supply");

#[derive(Serialize, Deserialize)]
pub enum ExecuteMsg {
    Mint { to: String, amount: Uint128 },
    Burn { from: String, amount: Uint128 },
}

#[entry_point]
pub fn execute(deps: DepsMut, _env: Env, info: MessageInfo, msg: ExecuteMsg) -> StdResult<Response> {
    match msg {
        ExecuteMsg::Mint { to, amount } => {
            // ❌ 未检查调用者是否为合约所有者
            let mut supply = TOTAL_SUPPLY.load(deps.storage)?;
            supply = supply.checked_add(amount)?;
            TOTAL_SUPPLY.save(deps.storage, &supply)?;

            Ok(Response::new()
                .add_attribute("action", "mint")
                .add_attribute("to", to)
                .add_attribute("amount", amount))
        }
        ExecuteMsg::Burn { from, amount } => {
            // ❌ 未检查授权
            let mut supply = TOTAL_SUPPLY.load(deps.storage)?;
            supply = supply.checked_sub(amount)?;
            TOTAL_SUPPLY.save(deps.storage, &supply)?;

            Ok(Response::new()
                .add_attribute("action", "burn")
                .add_attribute("from", from)
                .add_attribute("amount", amount))
        }
    }
}

修复代码

// ✅ 修复版本 — 所有权检查
fn assert_owner(deps: &DepsMut, sender: &Addr) -> StdResult<()> {
    let owner = OWNER.load(deps.storage)?;
    if sender.as_str() != owner.as_str() {
        return Err(StdError::generic_err("Unauthorized: only owner can call"));
    }
    Ok(())
}

#[entry_point]
pub fn execute(deps: DepsMut, _env: Env, info: MessageInfo, msg: ExecuteMsg) -> StdResult<Response> {
    match msg {
        ExecuteMsg::Mint { to, amount } => {
            assert_owner(&deps, &info.sender)?;
            let mut supply = TOTAL_SUPPLY.load(deps.storage)?;
            supply = supply.checked_add(amount)
                .map_err(|_| StdError::generic_err("Overflow"))?;
            TOTAL_SUPPLY.save(deps.storage, &supply)?;
            Ok(Response::new()
                .add_attribute("action", "mint")
                .add_attribute("to", &to)
                .add_attribute("amount", amount))
        }
        ExecuteMsg::Burn { from, amount } => {
            let owner = OWNER.load(deps.storage)?;
            if info.sender.as_str() != owner.as_str() && info.sender.as_str() != from.as_str() {
                return Err(StdError::generic_err("Unauthorized"));
            }
            let from_addr = deps.api.addr_validate(&from)?;
            let mut supply = TOTAL_SUPPLY.load(deps.storage)?;
            supply = supply.checked_sub(amount)
                .map_err(|_| StdError::generic_err("Underflow"))?;
            TOTAL_SUPPLY.save(deps.storage, &supply)?;
            Ok(Response::new()
                .add_attribute("action", "burn")
                .add_attribute("from", from)
                .add_attribute("amount", amount))
        }
    }
}

VULN-03: 整数溢出/下溢 (Integer Overflow/Underflow)

描述

CosmWasm 中 Rust 的整数运算在 debug 模式下会 panic 溢出,但在 release 模式下会 静默回绕。使用 Uint128 / Uint256 等类型时需注意。

类似 BatchOverflow 漏洞(2018年,多代币受影响),攻击者利用算术溢出绕过余额检查,铸造无限代币。

脆弱代码

// ⚠️ 脆弱版本 — 使用原始 u128 算术
pub fn unsafe_transfer(
    deps: DepsMut,
    sender: String,
    recipient: String,
    amount: u128,
) -> StdResult<Response> {
    // ❌ 使用原始 u128,release 模式下可能回绕
    let sender_balance = BALANCES
        .load(deps.storage, &sender)
        .unwrap_or(0u128);

    let recipient_balance = BALANCES
        .load(deps.storage, &recipient)
        .unwrap_or(0u128);

    // ❌ 减法可能下溢(release 模式下变成超大数)
    let new_sender = sender_balance - amount;
    // ❌ 加法可能上溢
    let new_recipient = recipient_balance + amount;

    BALANCES.save(deps.storage, &sender, &new_sender)?;
    BALANCES.save(deps.storage, &recipient, &new_recipient)?;

    Ok(Response::new().add_attribute("action", "unsafe_transfer"))
}

修复代码

// ✅ 修复版本 — 使用 checked 算术
use cosmwasm_std::Uint128;
use cw_storage_plus::Map;

const BALANCES: Map<&Addr, Uint128> = Map::new("balances");

pub fn safe_transfer(
    deps: DepsMut,
    sender: Addr,
    recipient: Addr,
    amount: Uint128,
) -> StdResult<Response> {
    let sender_balance = BALANCES
        .load(deps.storage, &sender)
        .unwrap_or(Uint128::zero());

    let new_sender = sender_balance
        .checked_sub(amount)
        .map_err(|_| StdError::generic_err("Insufficient balance"))?;

    let recipient_balance = BALANCES
        .load(deps.storage, &recipient)
        .unwrap_or(Uint128::zero());

    let new_recipient = recipient_balance
        .checked_add(amount)
        .map_err(|_| StdError::generic_err("Recipient balance overflow"))?;

    BALANCES.save(deps.storage, &sender, &new_sender)?;
    BALANCES.save(deps.storage, &recipient, &new_recipient)?;

    Ok(Response::new()
        .add_attribute("action", "safe_transfer")
        .add_attribute("amount", amount))
}

VULN-04: 存储碰撞与迁移漏洞 (Storage Collision in Migrations)

描述

CosmWasm 使用扁平键值存储。合约迁移时,如果新版本的存储键与旧版本冲突,可能导致数据损坏或权限提升。

类似 Parity 多签钱包库自杀漏洞 — 存储布局不兼容导致 50 万 ETH 被锁。

脆弱代码

// ⚠️ v1 合约 — 原始存储布局
const OWNER: Item<String> = Item::new("owner");
const BALANCES: Map<&Addr, Uint128> = Map::new("balances");

// ⚠️ v2 合约升级 — 存储碰撞!
const ADMIN: Item<String> = Item::new("owner"); // ❌ 与旧 OWNER 键碰撞
const AUTHORIZED: Map<&Addr, bool> = Map::new("auth");

#[entry_point]
pub fn migrate(deps: DepsMut, _env: Env, msg: MigrateMsg) -> StdResult<Response> {
    // ❌ 错误地读取了旧版本数据
    let old_owner = ADMIN.load(deps.storage)?;
    Ok(Response::new())
}

修复代码

// ✅ v1 合约 — 使用版本化键名
const V1_OWNER: Item<String> = Item::new("v1_owner");
const V1_BALANCES: Map<&Addr, Uint128> = Map::new("v1_balances");

// ✅ v2 合约 — 使用不同的键名前缀
const V2_ADMIN: Item<Addr> = Item::new("v2_admin");
const V2_AUTHORIZED: Map<&Addr, bool> = Map::new("v2_authorized");

#[entry_point]
pub fn migrate(deps: DepsMut, _env: Env, msg: MigrateMsg) -> StdResult<Response> {
    // ✅ 明确处理存储迁移
    let old_owner: String = V1_OWNER.load(deps.storage)?;
    let new_admin = deps.api.addr_validate(&old_owner)?;
    V2_ADMIN.save(deps.storage, &new_admin)?;

    Ok(Response::new()
        .add_attribute("action", "migrate")
        .add_attribute("from", "v1")
        .add_attribute("to", "v2"))
}

存储键命名约定

// ✅ 推荐的存储键命名模式
const CONTRACT_INFO: Item<ContractInfo> = Item::new("contract_info_v1");
const CONFIG: Item<Config> = Item::new("config_v1");
const BALANCES: Map<&Addr, Uint128> = Map::new("balance_v1");
const ALLOWANCES: Map<(&Addr, &Addr), Uint128> = Map::new("allowance_v1");
const STATE_V1: Item<StateV1> = Item::new("state_v1");

VULN-05: Reply 处理漏洞 (Reply-Based Attacks)

描述

Reply 是 CosmWasm 中处理子消息结果的关键机制。不正确的 reply 处理可导致状态不一致、资金损失或条件竞争。

类似 Lido 合约 reply 处理漏洞 — 子消息失败但主合约继续执行,导致 stETH 错误铸造。

脆弱代码

// ⚠️ 脆弱版本 — 忽略子消息执行结果
#[entry_point]
pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> StdResult<Response> {
    match msg.id {
        1 => {
            // ❌ 未检查 msg.result 是否成功!
            let data = msg.result.into_result().unwrap_or_default();
            let deposited: Uint128 = from_binary(&data.data.unwrap_or_default())?;

            // ❌ 即使子消息失败也更新状态
            let state = LOCKED_FUNDS.load(deps.storage)?;
            let new_state = state.checked_add(deposited)?;
            LOCKED_FUNDS.save(deps.storage, &new_state)?;

            Ok(Response::new()
                .add_attribute("reply_id", "1")
                .add_attribute("deposited", deposited))
        }
        _ => Ok(Response::new())
    }
}

修复代码

// ✅ 修复版本 — 始终检查 SubMsgResult
pub const DEPOSIT_REPLY_ID: u64 = 1;

#[entry_point]
pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> StdResult<Response> {
    match msg.id {
        DEPOSIT_REPLY_ID => {
            match msg.result {
                SubMsgResult::Ok(response) => {
                    let deposited: Uint128 = response
                        .data
                        .map(|d| from_binary(&d))
                        .transpose()?
                        .unwrap_or(Uint128::zero());

                    let state = LOCKED_FUNDS.load(deps.storage)?;
                    let new_state = state.checked_add(deposited)
                        .map_err(|_| StdError::generic_err("Overflow"))?;
                    LOCKED_FUNDS.save(deps.storage, &new_state)?;

                    Ok(Response::new()
                        .add_attribute("reply_id", "deposit")
                        .add_attribute("status", "success")
                        .add_attribute("deposited", deposited))
                }
                SubMsgResult::Error(e) => {
                    Ok(Response::new()
                        .add_attribute("reply_id", "deposit")
                        .add_attribute("status", "failed")
                        .add_attribute("error", e))
                }
            }
        }
        _ => Ok(Response::new())
    }
}

SubMsg 安全使用规范

// ✅ 安全的 SubMsg 模式
use cosmwasm_std::ReplyOn;

// 不需要回复
let msg = SubMsg {
    id: 0,
    msg: bank_msg,
    gas_limit: None,
    reply_on: ReplyOn::Never,
};

// 只需要成功通知
let msg = SubMsg {
    id: DEPOSIT_REPLY_ID,
    msg: cosmos_msg,
    gas_limit: Some(100_000), // ✅ 设置 gas 限制
    reply_on: ReplyOn::Success,
};

// 必须知道成功或失败
let msg = SubMsg {
    id: CRITICAL_REPLY_ID,
    msg: cosmos_msg,
    gas_limit: Some(500_000),
    reply_on: ReplyOn::Always,
};

VULN-06: IBC 超时漏洞 (IBC Timeout Exploits)

描述

IBC 使用超时机制防止资金锁定。如果超时处理不当,攻击者可利用时序攻击窃取跨链资产。

类似 Poly Network 跨链桥攻击(2021年,6.1亿美元损失) — 跨链消息验证不充分。

⚠️ MSG Chain 的跨链层目前处于规划阶段。以下适用于跨链功能启用后。

脆弱代码

// ⚠️ 脆弱版本 — IBC 超时处理不完整
#[entry_point]
pub fn ibc_packet_timeout(
    deps: DepsMut,
    _env: Env,
    msg: IbcPacketTimeoutMsg,
) -> StdResult<Response> {
    let packet = msg.packet;
    // ❌ 未验证 packet 来源
    // ❌ 未检查 packet 是否已被处理
    let mut escrow = ESCROW_BALANCE.load(deps.storage)?;
    let amount: Uint128 = from_binary(&packet.data)?;
    escrow = escrow.checked_sub(amount)
        .map_err(|_| StdError::generic_err("Insufficient escrow"))?;
    ESCROW_BALANCE.save(deps.storage, &escrow)?;

    Ok(Response::new())
}

修复代码

// ✅ 修复版本 — 完整的 IBC 超时处理
#[entry_point]
pub fn ibc_packet_timeout(
    deps: DepsMut,
    env: Env,
    msg: IbcPacketTimeoutMsg,
) -> StdResult<Response> {
    let packet = msg.packet;
    let config = CONFIG.load(deps.storage)?;

    // ✅ 1. 验证通道来源
    if packet.src.port_id != config.ibc_port
        || packet.src.channel_id != config.ibc_channel
    {
        return Err(StdError::generic_err("Invalid packet source"));
    }

    // ✅ 2. 检查序列号 — 防止重复处理
    let last_seq = LAST_TIMEOUT_SEQ.may_load(deps.storage)?.unwrap_or(0);
    if packet.sequence <= last_seq {
        return Err(StdError::generic_err("Packet sequence already processed"));
    }
    LAST_TIMEOUT_SEQ.save(deps.storage, &packet.sequence)?;

    // ✅ 3. 解析并验证数据
    let transfer_data: TransferData = from_binary(&packet.data)?;
    let refund_addr = deps.api.addr_validate(&transfer_data.sender)?;

    // ✅ 4. 执行退款
    let mut escrow = ESCROW_BALANCE.load(deps.storage)?;
    escrow = escrow.checked_sub(transfer_data.amount)
        .map_err(|_| StdError::generic_err("Insufficient escrow"))?;
    ESCROW_BALANCE.save(deps.storage, &escrow)?;

    let refund_msg = BankMsg::Send {
        to_address: refund_addr.to_string(),
        amount: vec![Coin::new(transfer_data.amount.u128(), &config.denom)],
    };

    Ok(Response::new()
        .add_message(refund_msg)
        .add_attribute("action", "ibc_timeout_refund")
        .add_attribute("sequence", packet.sequence.to_string()))
}

VULN-07: 价格预言机操纵 (Price Oracle Manipulation)

描述

使用链上价格预言机的合约易受操纵攻击,尤其是使用即时价格(如 AMM 池瞬时价格)时。

类似 TWAP 操纵攻击 — 攻击者通过大额交易操纵预言机价格以套利。

脆弱代码

// ⚠️ 脆弱版本 — 使用单一来源即时价格
pub fn calculate_collateral_value(
    deps: DepsMut,
    collateral_amount: Uint128,
    asset: String,
) -> StdResult<Uint128> {
    // ❌ 使用 AMM 池瞬时价格 — 极易操纵
    let price: Uint128 = PRICE_ORACLE.load(deps.storage, &asset)?;
    // ❌ 无滑点保护
    let value = collateral_amount.checked_mul(price)?;
    Ok(value)
}

修复代码

// ✅ 修复版本 — 使用 TWAP + 多重价格源
pub fn get_twap_price(
    deps: &DepsMut,
    asset: &str,
    current_time: u64,
) -> StdResult<Decimal> {
    let cumulative = PRICE_CUMULATIVE.load(deps.storage, asset)?;
    let time_elapsed = current_time - cumulative.last_update;

    if time_elapsed < 60 {
        return Err(StdError::generic_err("TWAP window too short"));
    }

    let twap = Decimal::from_ratio(cumulative.cumulative_price, time_elapsed);
    Ok(twap)
}

pub fn secure_liquidate(
    deps: DepsMut,
    env: Env,
    info: MessageInfo,
    borrower: String,
) -> StdResult<Response> {
    let position = POSITIONS.load(deps.storage, &borrower)?;

    // ✅ 使用 TWAP 价格
    let twap_price = get_twap_price(
        &deps,
        &position.collateral_asset,
        env.block.time.seconds(),
    )?;

    // ✅ 使用链下价格源交叉验证
    let external_price = query_external_price_source(
        deps.as_ref(),
        &position.collateral_asset,
    )?;

    // ✅ 取较低者(保守估值)
    let safe_price = if twap_price < external_price { twap_price } else { external_price };
    let collateral_value = position.collateral * safe_price;

    if collateral_value < position.debt.checked_mul(Uint128::new(120))? {
        // 执行清算
        Ok(Response::new().add_attribute("action", "liquidated"))
    } else {
        Err(StdError::generic_err("Position is healthy"))
    }
}

VULN-08: 闪电贷攻击 (Flash Loan Attacks)

描述

虽然 CosmWasm 本身不原生支持闪电贷,但通过复合消息操作可以实现类似攻击模式。

类似 bZx 闪电贷攻击(2020年) — 利用闪电贷操纵价格并在同一交易中获利。

脆弱代码

// ⚠️ 脆弱合约 — 未处理大规模临时价格冲击
pub fn swap_exact_in(
    deps: DepsMut,
    env: Env,
    info: MessageInfo,
    token_in: String,
    token_out: String,
    amount_in: Uint128,
    min_amount_out: Uint128,
) -> StdResult<Response> {
    let pool = POOL.load(deps.storage)?;
    // ❌ 未考虑大额 swap 对价格的影响
    let amount_out = pool.calculate_output(amount_in);
    if amount_out < min_amount_out {
        return Err(StdError::generic_err("Slippage exceeded"));
    }
    Ok(Response::new())
}

修复代码

// ✅ 修复版本 — TWAP + 滑点保护 + 交易检查
pub fn secure_swap(
    deps: DepsMut,
    env: Env,
    info: MessageInfo,
    token_in: String,
    token_out: String,
    amount_in: Uint128,
    min_amount_out: Uint128,
) -> StdResult<Response> {
    // ✅ 1. 使用 TWAP 检测价格偏离
    let twap_price = get_twap_price(&deps, &token_in, env.block.time.seconds())?;
    let current_price = get_current_price(&deps, &token_in)?;
    let deviation = (current_price - twap_price) / twap_price;

    if deviation > Decimal::percent(10) {
        return Err(StdError::generic_err("Price deviation too high"));
    }

    // ✅ 2. 防止同一交易中重入
    let last_tx = LAST_TX_HASH.load(deps.storage)?;
    if last_tx == env.transaction.unwrap().index.to_string() {
        return Err(StdError::generic_err("Reentrant transaction detected"));
    }
    LAST_TX_HASH.save(deps.storage, &env.transaction.unwrap().index.to_string())?;

    // ✅ 3. 强制执行滑点保护
    let pool = POOL.load(deps.storage)?;
    let amount_out = pool.calculate_output(amount_in);
    if amount_out < min_amount_out {
        return Err(StdError::generic_err("Slippage exceeded"));
    }

    Ok(Response::new().add_attribute("action", "secure_swap"))
}

VULN-09: 签名重放 (Signature Replay)

描述

MSG Chain 使用 Dilithium-5 签名,支持 nonce + ChainID 双重保护。自定义合约中的签名验证可能忽略这些保护。

类似 Wormhole 跨链桥攻击(2022年,3.26亿美元损失) — 签名验证逻辑缺失关键检查。

脆弱代码

// ⚠️ 脆弱版本 — 自定义签名验证缺少重放保护
pub fn execute_permit(
    deps: DepsMut,
    _env: Env,
    _info: MessageInfo,
    permit: Permit,
) -> StdResult<Response> {
    // ❌ 未验证 ChainID
    // ❌ 未验证 nonce
    let signer = verify_permit_signature(&deps, &permit)?;
    let balance = BALANCES.load(deps.storage, &signer)?;
    let new_balance = balance.checked_sub(permit.amount)?;
    BALANCES.save(deps.storage, &signer, &new_balance)?;
    Ok(Response::new())
}

修复代码

// ✅ 修复版本 — 完整的重放保护
const USED_NONCES: Map<(&Addr, u64), bool> = Map::new("used_nonce_v1");

pub fn execute_permit(
    deps: DepsMut,
    env: Env,
    _info: MessageInfo,
    permit: Permit,
) -> StdResult<Response> {
    // ✅ 1. ChainID 验证
    if env.block.chain_id != "msgchain-1" {
        return Err(StdError::generic_err("Invalid chain ID"));
    }

    // ✅ 2. 有效期验证
    if env.block.time.seconds() > permit.deadline {
        return Err(StdError::generic_err("Permit expired"));
    }

    // ✅ 3. Nonce 验证
    let signer = deps.api.addr_validate(&permit.signer)?;
    if USED_NONCES.has(deps.storage, (&signer, permit.nonce)) {
        return Err(StdError::generic_err("Permit already used"));
    }

    // ✅ 4. 签名验证
    let valid = verify_dilithium_signature(
        &deps, &permit.signer, &permit.message, &permit.signature,
    )?;
    if !valid {
        return Err(StdError::generic_err("Invalid signature"));
    }

    // ✅ 5. 标记 nonce 已使用(在状态变更前!)
    USED_NONCES.save(deps.storage, (&signer, permit.nonce), &true)?;

    // ✅ 6. 执行操作
    let balance = BALANCES.load(deps.storage, &signer)?;
    let new_balance = balance.checked_sub(permit.amount)?;
    BALANCES.save(deps.storage, &signer, &new_balance)?;

    Ok(Response::new()
        .add_attribute("action", "permit_executed")
        .add_attribute("nonce", permit.nonce.to_string()))
}

VULN-10: 随机数预测 (Randomness Prediction)

描述

CosmWasm 中无法生成安全的链上随机数。使用 env.block.* 或交易哈希等确定性输入作为随机数源是可预测的。

类似 SotM 黑客攻击 — 攻击者预测随机数赢得彩票合约。

脆弱代码

// ⚠️ 脆弱版本 — 可预测的随机数
pub fn lottery_pick_winner(deps: DepsMut, env: Env) -> StdResult<Response> {
    let players = PLAYERS.load(deps.storage)?;
    // ❌ 使用可预测的随机数源
    let pseudo_random = env.block.height ^ env.block.time.nanos();
    let winner_index = pseudo_random % players.len() as u64;
    let winner = &players[winner_index as usize];
    let prize = PRIZE_POOL.load(deps.storage)?;
    PRIZE_POOL.save(deps.storage, &Uint128::zero())?;
    Ok(Response::new()
        .add_message(BankMsg::Send {
            to_address: winner.to_string(),
            amount: vec![Coin::new(prize.u128(), "umsg")],
        }))
}

修复代码 — 承诺-揭示机制

// ✅ 修复 — Commit-Reveal 方案
const COMMITMENTS: Map<&Addr, Binary> = Map::new("commit_v1");

#[entry_point]
pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> StdResult<Response> {
    match msg {
        ExecuteMsg::Commit { hash } => {
            if COMMITMENTS.has(deps.storage, &info.sender) {
                return Err(StdError::generic_err("Already committed"));
            }
            COMMITMENTS.save(deps.storage, &info.sender, &hash)?;
            Ok(Response::new().add_attribute("action", "commit"))
        }
        ExecuteMsg::Reveal { secret } => {
            let committed = COMMITMENTS.load(deps.storage, &info.sender)?;
            let hash = deps.api.sha256(&secret);
            if hash != committed {
                return Err(StdError::generic_err("Invalid reveal"));
            }
            // 收集揭示用于最终随机数生成
            Ok(Response::new().add_attribute("action", "reveal"))
        }
        ExecuteMsg::Finalize => {
            // 使用所有揭示 + 区块数据生成最终随机数
            let final_random = deps.api.sha256(&[
                &combined_secrets[..],
                &env.block.height.to_be_bytes()[..],
                &env.block.time.nanos().to_be_bytes()[..],
            ].concat());
            Ok(Response::new().add_attribute("random", hex::encode(final_random)))
        }
    }
}

VULN-11: 抢先交易与 MEV (Front-Running / MEV)

描述

Mempool 中的交易对验证者/搜索者可见,他们可插入自己的交易以获取优势。

类似 Uniswap 三明治攻击 — 攻击者在用户交易前后插入交易以榨取价值。

脆弱代码

// ⚠️ 脆弱版本 — 易受抢先交易
pub fn submit_order(deps: DepsMut, env: Env, info: MessageInfo, order: Order) -> StdResult<Response> {
    // ❌ 立即执行,易被三明治攻击
    let result = execute_order(deps, &order)?;
    Ok(Response::new().add_attribute("action", "order_executed"))
}

修复代码

// ✅ 修复版本 — 批量拍卖模式
pub fn submit_order(deps: DepsMut, _env: Env, info: MessageInfo, order: Order) -> StdResult<Response> {
    let mut batch = BATCH_ORDERS.load(deps.storage)?;
    batch.push(BatchOrder {
        sender: info.sender,
        order,
        submitted_at: _env.block.time.seconds(),
    });
    BATCH_ORDERS.save(deps.storage, &batch)?;
    Ok(Response::new().add_attribute("action", "order_queued"))
}

pub fn execute_batch(deps: DepsMut, env: Env) -> StdResult<Response> {
    let batch = BATCH_ORDERS.load(deps.storage)?;
    let clearing_price = calculate_clearing_price(&batch)?;
    for order in batch {
        fill_order(deps.storage, order, clearing_price)?;
    }
    BATCH_ORDERS.save(deps.storage, &vec![])?;
    Ok(Response::new().add_attribute("action", "batch_executed"))
}

VULN-12: Gas Griefing (Gas 消耗攻击)

描述

攻击者通过触发高 gas 消耗操作使合约执行失败,导致用户损失 gas 费。MSG Chain 的 40/30/20/10 分配模式加剧了此问题。

脆弱代码

// ⚠️ 脆弱版本 — 无限迭代
pub fn process_deposits(deps: DepsMut, _env: Env, _info: MessageInfo, users: Vec<String>) -> StdResult<Response> {
    for user in &users {
        let deposit = USER_DEPOSITS.load(deps.storage, user)?;
        PROCESSED.save(deps.storage, user, &true)?;
    }
    Ok(Response::new().add_attribute("processed", users.len().to_string()))
}

修复代码

// ✅ 修复版本 — 限制迭代 + 分页
pub fn process_deposits_paginated(
    deps: DepsMut,
    _env: Env,
    _info: MessageInfo,
    start_after: Option<String>,
    limit: Option<u32>,
) -> StdResult<Response> {
    let limit = limit.unwrap_or(20).min(100); // ✅ 硬限制
    let users: Vec<String> = USER_DEPOSITS
        .keys(deps.storage, None, Some(limit), Order::Ascending)
        .collect();

    let mut total_deposited = Uint128::zero();
    for user in users {
        let deposit = USER_DEPOSITS.load(deps.storage, &user)?;
        total_deposited = total_deposited.checked_add(deposit)?;
        PROCESSED.save(deps.storage, &user, &true)?;
    }

    Ok(Response::new()
        .add_attribute("processed", users.len().to_string())
        .add_attribute("total", total_deposited))
}

VULN-13: 不安全的地址验证 (Unsafe Address Validation)

描述

CosmWasm 合约必须使用 deps.api.addr_validate() 验证地址,而不是 Addr::unchecked()。

脆弱代码

// ⚠️ 脆弱版本 — 未验证地址
pub fn transfer(
    deps: DepsMut,
    _env: Env,
    info: MessageInfo,
    recipient: String,
    amount: Uint128,
) -> StdResult<Response> {
    let recipient_addr = Addr::unchecked(&recipient); // ❌
    let sender_balance = BALANCES.load(deps.storage, &info.sender)?;
    BALANCES.save(deps.storage, &info.sender, &sender_balance.checked_sub(amount)?)?;
    Ok(Response::new()
        .add_message(BankMsg::Send {
            to_address: recipient_addr.to_string(),
            amount: vec![Coin::new(amount.u128(), "umsg")],
        }))
}

修复代码

// ✅ 修复版本 — 验证所有用户地址
pub fn secure_transfer(
    deps: DepsMut,
    _env: Env,
    info: MessageInfo,
    recipient: String,
    amount: Uint128,
) -> StdResult<Response> {
    let recipient_addr = deps.api.addr_validate(&recipient)?;
    let sender_balance = BALANCES.load(deps.storage, &info.sender)?;
    if sender_balance < amount {
        return Err(StdError::generic_err("Insufficient balance"));
    }
    BALANCES.save(deps.storage, &info.sender, &sender_balance.checked_sub(amount)?)?;
    Ok(Response::new()
        .add_message(BankMsg::Send {
            to_address: recipient_addr.to_string(),
            amount: vec![Coin::new(amount.u128(), "umsg")],
        })
        .add_attribute("action", "secure_transfer"))
}

VULN-14: funds 验证遗漏 (Missing Funds Validation)

描述

合约必须验证 info.funds 与操作金额一致,否则用户可利用错误金额执行操作。

脆弱代码

// ⚠️ 脆弱版本 — 未验证 funds
pub fn deposit(deps: DepsMut, _env: Env, info: MessageInfo, amount: Uint128) -> StdResult<Response> {
    // ❌ 用户传 1000 但只附上 1 umsg
    let mut balance = BALANCES.load(deps.storage, &info.sender)?;
    balance = balance.checked_add(amount)?;
    BALANCES.save(deps.storage, &info.sender, &balance)?;
    Ok(Response::new().add_attribute("action", "deposit").add_attribute("amount", amount))
}

修复代码

// ✅ 修复版本 — 验证 funds
pub fn secure_deposit(
    deps: DepsMut,
    _env: Env,
    info: MessageInfo,
    amount: Uint128,
    denom: String,
) -> StdResult<Response> {
    if info.funds.is_empty() {
        return Err(StdError::generic_err("No funds sent"));
    }
    let sent_amount = info.funds.iter()
        .find(|c| c.denom == denom)
        .map(|c| c.amount)
        .unwrap_or(Uint128::zero());
    if sent_amount != amount {
        return Err(StdError::generic_err(format!(
            "Sent {} {} but expected {}", sent_amount, denom, amount
        )));
    }
    let mut balance = BALANCES.load(deps.storage, &info.sender)?;
    balance = balance.checked_add(amount)?;
    BALANCES.save(deps.storage, &info.sender, &balance)?;
    Ok(Response::new()
        .add_attribute("action", "secure_deposit")
        .add_attribute("amount", amount))
}

VULN-15: 不安全的跨合约查询 (Unsafe Cross-Contract Queries)

描述

查询外部合约时,如果外部合约返回恶意数据,可能导致反序列化攻击或逻辑错误。

脆弱代码

// ⚠️ 脆弱版本 — 未验证查询结果
pub fn get_asset_price(deps: DepsMut, oracle_addr: String) -> StdResult<Uint128> {
    // ❌ 未验证预言机地址和返回值
    let price: Uint128 = deps.querier.query_wasm_smart(
        oracle_addr,
        &OracleQuery::GetPrice {},
    )?;
    Ok(price)
}

修复代码

// ✅ 修复版本 — 验证跨合约查询结果
pub fn secure_get_asset_price(
    deps: DepsMut,
    oracle_addr: String,
) -> StdResult<Uint128> {
    let config = CONFIG.load(deps.storage)?;
    if !config.trusted_oracles.contains(&oracle_addr) {
        return Err(StdError::generic_err("Untrusted oracle"));
    }

    let price: Uint128 = deps.querier.query_wasm_smart(
        &deps.api.addr_validate(&oracle_addr)?,
        &OracleQuery::GetPrice {},
    )?;

    if price.is_zero() {
        return Err(StdError::generic_err("Oracle returned zero price"));
    }
    if price > Uint128::new(1_000_000_000) {
        return Err(StdError::generic_err("Oracle price exceeds maximum"));
    }

    Ok(price)
}

VULN-16: 不安全的 IBC 包验证 (Unsafe IBC Packet Validation)

描述

IBC 包处理时未验证发送者、通道或顺序,导致伪造跨链消息。

脆弱代码

// ⚠️ 脆弱版本 — 未验证 IBC 包
#[entry_point]
pub fn ibc_packet_receive(
    deps: DepsMut,
    env: Env,
    msg: IbcPacketReceiveMsg,
) -> StdResult<IbcReceiveResponse> {
    let packet = msg.packet;
    let data: TransferData = from_binary(&packet.data)?;
    mint_tokens(deps, &data.recipient, data.amount)?;
    Ok(IbcReceiveResponse::new().add_attribute("action", "mint"))
}

修复代码

// ✅ 修复版本 — 完整的 IBC 包验证
#[entry_point]
pub fn ibc_packet_receive(
    deps: DepsMut,
    env: Env,
    msg: IbcPacketReceiveMsg,
) -> StdResult<IbcReceiveResponse> {
    let packet = msg.packet;
    let config = CONFIG.load(deps.storage)?;

    // ✅ 1. 验证端口和通道
    if packet.dest.port_id != config.ibc_port
        || packet.dest.channel_id != config.ibc_channel {
        return Err(StdError::generic_err("Invalid destination"));
    }
    if packet.src.port_id != config.counterparty_port
        || packet.src.channel_id != config.counterparty_channel {
        return Err(StdError::generic_err("Invalid source"));
    }

    // ✅ 2. 验证序列号
    let last_seq = LAST_RECEIVED_SEQ.load(deps.storage)?;
    if packet.sequence <= last_seq {
        return Err(StdError::generic_err("Duplicate packet"));
    }
    LAST_RECEIVED_SEQ.save(deps.storage, &packet.sequence)?;

    // ✅ 3. 验证超时
    if packet.timeout.has_expired(&env) {
        return Err(StdError::generic_err("Packet expired"));
    }

    // ✅ 4. 验证数据
    let data: TransferData = from_binary(&packet.data)?;
    let recipient = deps.api.addr_validate(&data.recipient)?;
    if data.amount.is_zero() {
        return Err(StdError::generic_err("Zero amount"));
    }

    mint_tokens(deps, &recipient, data.amount)?;
    Ok(IbcReceiveResponse::new()
        .add_attribute("action", "mint")
        .add_attribute("sequence", packet.sequence.to_string()))
}

VULN-17: 无限制代币增发 (Unchecked Token Minting)

描述

合约中铸币函数缺少权限控制或上限检查,导致任意用户可无限铸造代币。

脆弱代码

// ⚠️ 脆弱版本 — 无限制铸币
pub fn mint(deps: DepsMut, _env: Env, info: MessageInfo, to: String, amount: Uint128) -> StdResult<Response> {
    // ❌ 无权限检查!无总量上限!
    let mut supply = TOTAL_SUPPLY.load(deps.storage)?;
    supply = supply.checked_add(amount)?;
    TOTAL_SUPPLY.save(deps.storage, &supply)?;
    let mut balance = BALANCES.load(deps.storage, &to).unwrap_or(Uint128::zero());
    balance = balance.checked_add(amount)?;
    BALANCES.save(deps.storage, &to, &balance)?;
    Ok(Response::new()
        .add_attribute("action", "mint")
        .add_attribute("to", to)
        .add_attribute("amount", amount))
}

修复代码

// ✅ 修复版本 — 权限 + 上限控制
pub const MAX_SUPPLY: Uint128 = Uint128::new(1_000_000_000_000_000);

pub fn secure_mint(
    deps: DepsMut,
    _env: Env,
    info: MessageInfo,
    to: String,
    amount: Uint128,
) -> StdResult<Response> {
    let config = CONFIG.load(deps.storage)?;
    if info.sender != config.minter {
        return Err(StdError::generic_err("Unauthorized: not a minter"));
    }
    let to_addr = deps.api.addr_validate(&to)?;
    let supply = TOTAL_SUPPLY.load(deps.storage)?;
    let new_supply = supply.checked_add(amount)
        .map_err(|_| StdError::generic_err("Supply overflow"))?;
    if new_supply > MAX_SUPPLY {
        return Err(StdError::generic_err("Exceeds maximum supply"));
    }
    TOTAL_SUPPLY.save(deps.storage, &new_supply)?;
    let mut balance = BALANCES.load(deps.storage, &to_addr).unwrap_or(Uint128::zero());
    balance = balance.checked_add(amount)?;
    BALANCES.save(deps.storage, &to_addr, &balance)?;
    Ok(Response::new()
        .add_attribute("action", "secure_mint")
        .add_attribute("amount", amount)
        .add_attribute("new_supply", new_supply))
}

VULN-18: 权限提升 (Privilege Escalation)

描述

合约中存储权限或角色时使用了可变存储,攻击者可通过更新特定状态获得未授权权限。

脆弱代码

// ⚠️ 脆弱版本 — 角色存储可被篡改
const ROLES: Map<&Addr, String> = Map::new("roles");

pub fn has_role(deps: Deps, addr: &Addr, role: &str) -> bool {
    ROLES.may_load(deps.storage, addr).unwrap_or(None)
        .map(|r| r == role).unwrap_or(false)
}

#[entry_point]
pub fn migrate(deps: DepsMut, _env: Env, msg: MigrateMsg) -> StdResult<Response> {
    // ❌ 存储操作可能被利用设置任意角色
    ROLES.save(deps.storage, &Addr::unchecked("attacker"), &"admin".to_string())?;
    Ok(Response::new())
}

修复代码

// ✅ 修复版本 — 不可变权限 + 硬编码角色检查
pub struct Config {
    pub owner: Addr,
    pub admin: Addr,
    pub operators: Vec<Addr>,
}

const CONFIG: Item<Config> = Item::new("config_v1");

pub fn assert_admin(deps: &Deps, sender: &Addr) -> StdResult<()> {
    let config = CONFIG.load(deps.storage)?;
    if *sender != config.admin && *sender != config.owner {
        return Err(StdError::generic_err("Unauthorized: not admin"));
    }
    Ok(())
}

pub fn assert_operator(deps: &Deps, sender: &Addr) -> StdResult<()> {
    let config = CONFIG.load(deps.storage)?;
    if *sender == config.owner || *sender == config.admin { return Ok(()); }
    if config.operators.contains(sender) { return Ok(()); }
    Err(StdError::generic_err("Unauthorized: not operator"))
}

VULN-19: 精度损失 (Precision Loss)

描述

使用 Decimal 类型时可能因精度问题导致价值损耗或分配不均。

脆弱代码

// ⚠️ 脆弱版本 — 精度损失
pub fn distribute_rewards(deps: DepsMut, total_reward: Uint128) -> StdResult<Response> {
    let stakers = STAKERS.load(deps.storage)?;
    let total_staked = TOTAL_STAKED.load(deps.storage)?;

    for (staker, stake) in &stakers {
        // ❌ 多次舍入导致总分配 !== total_reward
        let share = Decimal::from_ratio(*stake, total_staked);
        let reward = total_reward * share;
        distribute(deps, staker, reward)?;
    }
    Ok(Response::new())
}

修复代码

// ✅ 修复版本 — 余数处理
pub fn secure_distribute_rewards(deps: DepsMut, total_reward: Uint128) -> StdResult<Response> {
    let stakers = STAKERS.load(deps.storage)?;
    let total_staked = TOTAL_STAKED.load(deps.storage)?;

    let mut distributed = Uint128::zero();
    let staker_count = stakers.len();

    for (i, (staker, stake)) in stakers.iter().enumerate() {
        // ✅ 最后一个参与者获得所有余数
        if i == staker_count - 1 {
            let final_reward = total_reward.checked_sub(distributed)?;
            distribute(deps, staker, final_reward)?;
        } else {
            let raw_share = stake.u128() * total_reward.u128();
            let reward = Uint128::new(raw_share / total_staked.u128());
            distributed = distributed.checked_add(reward)?;
            distribute(deps, staker, reward)?;
        }
    }
    Ok(Response::new().add_attribute("action", "distribute_rewards"))
}

VULN-20: Agent API 滥用 (Agent API Abuse)

描述

MSG Chain 的 Agent API 允许 AI Agent 执行链上操作。如果验证不严,攻击者可通过恶意 Agent 执行越权操作。

脆弱代码

// ⚠️ 脆弱版本 — Agent 操作缺少宪法约束
#[entry_point]
pub fn execute_agent_action(
    deps: DepsMut,
    env: Env,
    info: MessageInfo,
    action: AgentAction,
) -> StdResult<Response> {
    // ❌ 未验证 Agent 身份和宪法
    match action {
        AgentAction::Transfer { recipient, amount } => {
            return Ok(Response::new()
                .add_message(BankMsg::Send {
                    to_address: recipient,
                    amount: vec![Coin::new(amount, "umsg")],
                }));
        }
    }
}

修复代码

// ✅ 修复版本 — 完整的 Agent 验证
#[entry_point]
pub fn execute_agent_action(
    deps: DepsMut,
    env: Env,
    info: MessageInfo,
    action: AgentAction,
) -> StdResult<Response> {
    // ✅ 1. 验证 Agent 存在且属于调用者
    let agent = AGENTS.load(deps.storage, &info.sender)?;
    if agent.owner != info.sender {
        return Err(StdError::generic_err("Agent not owned by sender"));
    }

    // ✅ 2. 检查宪法约束 (CONSTITUTION_VIOLATION)
    if let Some(constitution) = &agent.constitution {
        if !constitution.allows_action(&action) {
            return Err(StdError::generic_err("CONSTITUTION_VIOLATION"));
        }
    }

    // ✅ 3. 检查 Agent 状态
    if agent.paused {
        return Err(StdError::generic_err("Agent is paused"));
    }

    // ✅ 4. 验证操作权限
    let allowed_actions = agent.get_allowed_actions();
    if !allowed_actions.contains(&action.action_type()) {
        return Err(StdError::generic_err("Action not permitted"));
    }

    // ✅ 5. 执行(X-MSG-Stub 审计标记)
    AUDIT_LOG.save(deps.storage, &AuditEntry {
        agent_id: agent.id,
        action: action.clone(),
        timestamp: env.block.time.seconds(),
    })?;

    Ok(Response::new()
        .add_attribute("action", "agent_executed")
        .add_attribute("x_msg_stub", "true"))
}

3. MSG Chain 特有安全考量

3.1 Dilithium-5 签名验证

Gas 成本影响

操作                Gas 消耗 (估计)      对比 ECDSA
─────────────────────────────────────────────────────
Dilithium-5 签名验证  ~150,000 - 300,000  ~5-10x ECDSA
ECDSA 签名验证          ~30,000 - 50,000  基准线
公钥序列化/反序列化     ~20,000 - 40,000  ~10x ECDSA

安全含义:

签名验证模式

// ✅ Dilithium-5 签名验证模式
pub fn verify_agent_signature(
    deps: &DepsMut,
    agent: &Agent,
    message: &[u8],
    signature: &DilithiumSignature,
) -> StdResult<bool> {
    let pk = DilithiumPublicKey::from_bytes(&agent.public_key)
        .map_err(|_| StdError::generic_err("Invalid public key"))?;
    let valid = deps.api.dilithium5_verify(message, signature, &pk)?;
    Ok(valid)
}

3.2 DAR 共识操纵攻击

共识模型

DAR (Dynamic Authority Rotation) 验证者集轮换:

潜在攻击向量

攻击类型 描述 风险等级
Stake 集中化 攻击者控制 > 1/3 总 stake 高
轮换抢占 在轮换窗口内快速获取验证者席位 中
长程攻击 攻击者从过去某点分叉链 低 (BadgerDB 无历史)
Nothing-at-Stake 验证者在多个分叉上出块 低 (DAR 缓解)

安全建议

// 验证者集变更监测
pub fn monitor_validator_set(
    deps: DepsMut,
    _env: Env,
    _info: MessageInfo,
) -> StdResult<Response> {
    let current_set = VALIDATOR_SET.load(deps.storage)?;
    let pending_set = PENDING_VALIDATOR_SET.may_load(deps.storage)?;

    if let Some(pending) = pending_set {
        let confirmations = pending.confirmation_count;
        if confirmations >= 104 || (current_set.size() >= 96 && confirmations >= 96) {
            execute_rotation(deps, pending)?;
        }
    }
    Ok(Response::new().add_attribute("action", "monitor_validators"))
}

3.3 BadgerDB vs IAVL 存储安全差异

特性 BadgerDB (MSG Chain) IAVL (Cosmos SDK)
存储模型 LSM-Tree 平衡二叉树
证据 (Proof) 无原生支持 Merkle 证据
状态快照 高效 (LSM) 较慢
历史状态 需额外配置 原生支持
并发性 高 中
加密 支持 不支持

安全差异

// BadgerDB 无 IAVL 的 Merkle 证据
// → 轻客户端验证需要额外机制

// BadgerDB 垃圾回收
pub fn badger_gc_maintenance(deps: DepsMut) -> StdResult<Response> {
    // MSG Chain 节点定期 GC
    Ok(Response::new().add_attribute("action", "badger_gc"))
}

// 加密配置
pub struct BadgerConfig {
    pub encryption_enabled: bool,
    pub key_rotation_days: u64,
}

3.4 Agent API 安全边界

已知安全边界

Agent API 写路径    X-MSG-Stub=true (未审计存根标记)
@msg-chain/sdk      alpha 阶段,未审计
Agent 宪法检查       错误码 12: CONSTITUTION_VIOLATION
Agent 发现           错误码 10: AGENT_NOT_FOUND

Agent 安全通信

// Agent 间安全通信
pub struct AgentMessage {
    pub sender_id: String,
    pub recipient_id: String,
    pub nonce: Uint128,
    pub timestamp: u64,
    pub message_type: MessageType,
    pub payload: Binary,
    pub signature: Vec<u8>,
}

pub fn verify_agent_message(deps: &DepsMut, msg: &AgentMessage) -> StdResult<bool> {
    let sender_agent = AGENTS.load(deps.storage, &msg.sender_id)?;

    // 验证时间戳 (最大偏移 5 分钟)
    let current_time = mock_env().block.time.seconds();
    if msg.timestamp > current_time || current_time - msg.timestamp > 300 {
        return Ok(false);
    }

    // 验证 nonce
    if USED_NONCES.has(deps.storage, (&msg.sender_id, msg.nonce.u64())) {
        return Ok(false);
    }

    // 验证签名
    let message_bytes = msg.get_signing_bytes();
    let sig = DilithiumSignature::from_bytes(&msg.signature)
        .map_err(|_| StdError::generic_err("Invalid signature format"))?;
    deps.api.dilithium5_verify(&message_bytes, &sig, &sender_agent.public_key)
}

3.5 Constitution 违规攻击面

Agent 宪法是约束 Agent 行为的规则集。

宪法验证

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct Constitution {
    pub allowed_actions: Vec<ActionType>,
    pub max_transfer_amount: Uint128,
    pub restricted_addresses: Vec<Addr>,
    pub required_approvals: u32,
}

pub fn strict_constitution_check(
    constitution: &Constitution,
    action: &AgentAction,
) -> StdResult<()> {
    // 1. 验证操作类型
    if !constitution.allowed_actions.contains(&action.action_type()) {
        return Err(StdError::generic_err("CONSTITUTION_VIOLATION"));
    }

    // 2. 检查转账金额
    if let AgentAction::Transfer { amount, .. } = action {
        if Uint128::new(*amount) > constitution.max_transfer_amount {
            return Err(StdError::generic_err("CONSTITUTION_VIOLATION: Exceeds max"));
        }
    }

    // 3. 检查限制地址
    if let AgentAction::Transfer { recipient, .. } = action {
        let recipient_addr = Addr::unchecked(recipient);
        if constitution.restricted_addresses.contains(&recipient_addr) {
            return Err(StdError::generic_err("CONSTITUTION_VIOLATION: Restricted"));
        }
    }

    // 4. 检查多签批准
    if constitution.required_approvals > 0 {
        let approvals = ACTION_APPROVALS.load(deps.storage, &action.id())?;
        if approvals < constitution.required_approvals {
            return Err(StdError::generic_err("CONSTITUTION_VIOLATION: Need approvals"));
        }
    }

    Ok(())
}

4. 合约安全审计检查表

50+ 审计项,按类别组织。每个审计项包含:检查 ID、描述、严重等级、验证方法。

4.1 访问控制 (Access Control)

ID 检查项 严重等级 验证方法
AC-01 管理员函数是否验证调用者权限 🔴 Critical 代码审查每处 execute 入口
AC-02 是否使用 addr_validate() 验证地址 🟠 High grep Addr::unchecked
AC-03 角色管理是否有提权保护 🟠 High 测试角色升级路径
AC-04 迁移函数是否有额外权限检查 🔴 Critical 审查 migrate 入口
AC-05 Owner 地址是否可通过外部调用修改 🟠 High 检查 owner setter 权限
AC-06 暂停/恢复功能是否只对管理员开放 🟡 Medium 测试 pause/unpause
AC-07 代理合约的调用者是否为预期合约 🟠 High 审查代理模式
AC-08 Agent API 是否验证 Agent 身份 🔴 Critical 审查 agent 调用路径

4.2 算术与精度 (Arithmetic)

ID 检查项 严重等级 验证方法
AR-01 是否使用 checked_add/sub/mul 🔴 Critical grep + 和 - 操作符
AR-02 除零保护 🟠 High 检查除法前分母验证
AR-03 Decimal 精度损失 🟡 Medium 计算 total_in = total_out
AR-04 费率计算舍入方向是否正确 🟡 Medium 测试边界值
AR-05 Uint128 转换为 u64 时 checked 转换 🟠 High grep as u64 / as u128
AR-06 批量分配时余数处理 🟡 Medium 验证分配总和 = 初始总和

4.3 重入与执行流 (Reentrancy)

ID 检查项 严重等级 验证方法
RE-01 Checks-Effects-Interactions 模式 🔴 Critical 审查状态更新与消息发送顺序
RE-02 Reply handler 检查 SubMsgResult 🟠 High 审查所有 reply 函数
RE-03 SubMsg 的 reply_on 设置是否正确 🟡 Medium 审查 SubMsg 构造
RE-04 状态锁 (mutex) 是否有效 🟡 Medium 测试并发场景
RE-05 跨合约调用是否导致重入 🔴 Critical 追踪所有跨合约消息流

4.4 代币与经济 (Token/Economics)

ID 检查项 严重等级 验证方法
TK-01 铸币有无总量上限 🔴 Critical 审查 mint 函数
TK-02 销毁有无最小余额限制 🟡 Medium 审查 burn 函数
TK-03 转账是否验证 sender 余额 🔴 Critical 审查 transfer 函数
TK-04 授权 (allowance) 是否正确扣减 🟠 High 测试 approve-transfer
TK-05 代币销毁是否同步减少总供应量 🟠 High 审查 burn + supply
TK-06 是否支持原生代币与 CW20 混合 🟡 Medium 审查 fund 处理
TK-07 锁定代币是否可强制转移 🟠 High 审查锁仓逻辑

4.5 IBC 跨链安全 (IBC)

ID 检查项 严重等级 验证方法
IB-01 IBC 包是否验证端口和通道 🔴 Critical 审查 ibc_packet_receive
IB-02 IBC 包序列号是否去重 🔴 Critical 审查序列号跟踪
IB-03 IBC 超时处理是否完整 🟠 High 审查 ibc_packet_timeout
IB-04 IBC ACK 处理是否验证 🟠 High 审查 ibc_packet_ack
IB-05 跨链转账铸币/销毁是否对称 🔴 Critical 端到端 IBC 测试
IB-06 IBC 轻客户端验证是否跳过 🔴 Critical 审查 ics20/ics721

4.6 存储安全 (Storage)

ID 检查项 严重等级 验证方法
ST-01 存储键是否使用版本化前缀 🟡 Medium 审查所有 Item/Map 键
ST-02 迁移函数是否处理旧存储键 🟠 High 审查 migrate 函数
ST-03 是否存在存储键碰撞 🟠 High 分析所有存储键
ST-04 敏感数据是否存储在链上 🟡 Medium 审查存储数据结构
ST-05 BadgerDB 的 TTL 设置是否合理 🟢 Low 审查过期数据策略
ST-06 存储迭代是否限制数量 🟡 Medium 审查 range / keys 调用

4.7 输入验证 (Input Validation)

ID 检查项 严重等级 验证方法
IV-01 所有 String 输入是否经 addr_validate 🟠 High grep addr_validate
IV-02 金额是否为 0 或负数 🟡 Medium 审查金额检查
IV-03 数组/向量是否为空 🟢 Low 审查空集合处理
IV-04 分页参数是否有限制 🟡 Medium 审查分页函数
IV-05 Denom 是否在白名单中 🟠 High 审查 denom 验证
IV-06 智能合约地址是否有效 🟡 Medium 审查合约地址检查
IV-07 非空字符串验证 🟢 Low 审查字符串空值

4.8 事件与日志 (Events)

ID 检查项 严重等级 验证方法
EV-01 所有状态变更是否发出事件 🟡 Medium 审查状态变更无事件
EV-02 事件属性是否包含关键信息 🟢 Low 审查 event attribute
EV-03 敏感数据是否泄露在事件中 🟠 High 审查事件中的 secret
EV-04 错误处理是否泄露存储路径 🟢 Low 审查错误消息

4.9 MSG Chain 特有安全

ID 检查项 严重等级 验证方法
MS-01 Dilithium-5 签名验证正确处理非 ASCII 🟠 High 审查签名验证路径
MS-02 Gas 估算是否考虑 Dilithium-5 成本 🟡 Medium 基准测试
MS-03 Agent Constitution 检查是否完整 🔴 Critical 审查宪法验证
MS-04 X-MSG-Stub 标记的写路径是否限制 🔴 Critical 审查 stub 标记路径
MS-05 Agent 通信 nonce 是否唯一 🟠 High 审查 agent 消息 nonce
MS-06 DAR 验证者集变化是否在阈值内 🟡 Medium 审查阈值检查
MS-07 @msg-chain/sdk 使用最新版本 🟡 Medium 检查 sdk 版本
MS-08 是否在正式测试网测试 🟢 Low 检查测试环境
MS-09 Explorer hash 搜索 503 处理 🟡 Medium 审查 explorer 回调
MS-10 Nonce + ChainID 重放保护是否生效 🔴 Critical 审查 nonce/chainid 检查

4.10 紧急停止与升级

ID 检查项 严重等级 验证方法
EM-01 是否有暂停/紧急停止功能 🔴 Critical 审查 pause 机制
EM-02 暂停功能是否保护关键状态 🟠 High 测试暂停后操作
EM-03 升级是否需要治理通过 🟠 High 审查迁移治理
EM-04 紧急提现功能是否安全 🔴 Critical 审查 emergency withdraw
EM-05 多签是否实现 🟠 High 审查多签逻辑

5. 安全编码规范

5.1 输入验证模式

// ✅ 输入验证最佳实践
pub fn validate_inputs(
    deps: DepsMut,
    recipient: &str,
    amount: Uint128,
    denom: &str,
) -> StdResult<Addr> {
    let recipient_addr = deps.api.addr_validate(recipient)?;
    if amount.is_zero() {
        return Err(StdError::generic_err("Amount must be positive"));
    }
    if !is_valid_denom(denom) {
        return Err(StdError::generic_err("Invalid denomination"));
    }
    Ok(recipient_addr)
}

fn is_valid_denom(denom: &str) -> bool {
    let valid_denoms = ["umsg", "uconst", "uagent"];
    valid_denoms.contains(&denom)
}

5.2 授权模式

// ✅ 所有权模式
pub fn assert_owner(deps: &Deps, sender: &Addr) -> StdResult<()> {
    let config = CONFIG.load(deps.storage)?;
    if sender != &config.owner {
        return Err(StdError::generic_err("Unauthorized"));
    }
    Ok(())
}

// ✅ RBAC 模式
pub enum Role { Admin, Operator, User }

pub fn assert_role(deps: &Deps, sender: &Addr, required_role: Role) -> StdResult<()> {
    let config = CONFIG.load(deps.storage)?;
    match required_role {
        Role::Admin => {
            if sender != &config.admin && sender != &config.owner {
                return Err(StdError::generic_err("Admin role required"));
            }
        }
        Role::Operator => {
            if sender != &config.owner && sender != &config.admin
                && !config.operators.contains(sender) {
                return Err(StdError::generic_err("Operator role required"));
            }
        }
        Role::User => {}
    }
    Ok(())
}

5.3 安全数学操作

// ✅ 安全数学模式
pub struct SafeMath;

impl SafeMath {
    pub fn add(a: Uint128, b: Uint128) -> StdResult<Uint128> {
        a.checked_add(b).map_err(|_| StdError::generic_err("Addition overflow"))
    }
    pub fn sub(a: Uint128, b: Uint128) -> StdResult<Uint128> {
        a.checked_sub(b).map_err(|_| StdError::generic_err("Subtraction underflow"))
    }
    pub fn mul(a: Uint128, b: Uint128) -> StdResult<Uint128> {
        a.checked_mul(b).map_err(|_| StdError::generic_err("Multiplication overflow"))
    }
    pub fn div(a: Uint128, b: Uint128) -> StdResult<Decimal> {
        if b.is_zero() { return Err(StdError::generic_err("Division by zero")); }
        Ok(Decimal::from_ratio(a, b))
    }
}

5.4 事件 (Event) 规范

// ✅ 事件发射标准模式
pub fn emit_transfer_event(
    response: &mut Response,
    from: &Addr,
    to: &Addr,
    amount: Uint128,
    denom: &str,
) {
    response.attributes.push(Attribute::new("action", "transfer"));
    response.attributes.push(Attribute::new("from", from));
    response.attributes.push(Attribute::new("to", to));
    response.attributes.push(Attribute::new("amount", amount.to_string()));
    response.attributes.push(Attribute::new("denom", denom));
}

// ❌ 避免:
// - 不记录操作类型
// - 泄露敏感数据(密码、私钥)
// - 使用不稳定的值作为 key
// - 记录大量数据

5.5 错误处理模式

// ✅ 使用 thiserror 定义合约特有错误
use cosmwasm_std::StdError;
use thiserror::Error;

#[derive(Error, Debug, PartialEq)]
pub enum ContractError {
    #[error("{0}")]
    Std(#[from] StdError),

    #[error("Unauthorized: {reason}")]
    Unauthorized { reason: String },

    #[error("Insufficient funds: required {required}, available {available}")]
    InsufficientFunds { required: Uint128, available: Uint128 },

    #[error("Invalid input: {reason}")]
    InvalidInput { reason: String },

    #[error("Contract is paused")]
    ContractPaused,

    #[error("Constitution violation: {detail}")]
    ConstitutionViolation { detail: String },
}

// ✅ 错误处理模式
pub fn process_with_error_handling(
    deps: DepsMut,
    info: MessageInfo,
    amount: Uint128,
) -> Result<Response, ContractError> {
    if amount.is_zero() {
        return Err(ContractError::InvalidInput {
            reason: "Amount must be positive".to_string(),
        });
    }
    let config = CONFIG.load(deps.storage)?;
    let balance = BALANCES.load(deps.storage, &info.sender)
        .map_err(|_| ContractError::Unauthorized {
            reason: "Account not found".to_string(),
        })?;
    if amount > balance {
        return Err(ContractError::InsufficientFunds {
            required: amount,
            available: balance,
        });
    }
    Ok(Response::new())
}

5.6 升级安全模式

// ✅ 安全升级模式
#[derive(Serialize, Deserialize)]
pub enum ContractVersion { V1, V2 { migration_timestamp: u64 } }

const VERSION: Item<ContractVersion> = Item::new("contract_version");

#[entry_point]
pub fn migrate(deps: DepsMut, _env: Env, msg: MigrateMsg) -> StdResult<Response> {
    let version = VERSION.load(deps.storage)?;
    match version {
        ContractVersion::V1 => migrate_v1_to_v2(deps, msg)?,
        ContractVersion::V2 { .. } => {
            return Err(StdError::generic_err("Already at latest version"));
        }
    }
    Ok(Response::new())
}

fn migrate_v1_to_v2(deps: DepsMut, msg: MigrateMsg) -> StdResult<()> {
    let old_config: V1Config = V1_CONFIG.load(deps.storage)?;
    let new_config = V2Config {
        owner: old_config.admin,
        authorized: old_config.authorized,
        version: 2,
        migration_timestamp: msg.timestamp,
    };
    V2_CONFIG.save(deps.storage, &new_config)?;
    VERSION.save(deps.storage, &ContractVersion::V2 {
        migration_timestamp: msg.timestamp,
    })?;
    Ok(())
}

// 紧急回滚
#[entry_point]
pub fn sudo(deps: DepsMut, _env: Env, msg: SudoMsg) -> StdResult<Response> {
    match msg {
        SudoMsg::Rollback => {
            V2_CONFIG.remove(deps.storage);
            VERSION.save(deps.storage, &ContractVersion::V1)?;
            Ok(Response::new().add_attribute("action", "rollback"))
        }
    }
}

6. 审计工具

6.1 Rust/CosmWasm 静态分析

# 1. Clippy — Rust lint
cargo clippy -- -D warnings

# 2. RustSec — 依赖安全审计
cargo install cargo-audit
cargo audit

# 3. CosmWasm 检查器
cargo install cosmwasm-check
cosmwasm-check ./target/wasm32-unknown-unknown/release/*.wasm

6.2 手动审计检查流程

# 关键搜索模式
echo "=== 检查 Addr::unchecked ==="
rg "Addr::unchecked" --type rust
echo "=== 检查 unchecked 算术 ==="
rg "[+-]" --type rust | rg -v "checked_|wrapping_|saturating_"
echo "=== 检查 unwrap ==="
rg "\.unwrap\(\)" --type rust
echo "=== 检查 SubMsg ==="
rg "SubMsg" --type rust
echo "=== 检查 storage 键碰撞 ==="
rg 'Item::new\("' . | sort
rg 'Map::new\("' . | sort

6.3 Fuzz 测试

// ✅ Fuzz 测试示例
#[cfg(test)]
mod fuzz_tests {
    use proptest::prelude::*;

    proptest! {
        #![proptest_config = ProptestConfig {
            cases: 1000,
            .. ProptestConfig::default()
        }]

        #[test]
        fn fuzz_transfer_amounts(
            sender_balance in 0..1_000_000u128,
            transfer_amount in 0..2_000_000u128,
        ) {
            let mut deps = mock_dependencies();
            let env = mock_env();
            let sender = Addr::unchecked("sender");
            let recipient = Addr::unchecked("recipient");

            BALANCES.save(
                deps.as_mut().storage, &sender, &Uint128::new(sender_balance),
            ).unwrap();

            let result = transfer(
                deps.as_mut(), env,
                mock_info(&sender.to_string(), &[]),
                recipient.to_string(), Uint128::new(transfer_amount),
            );

            if result.is_ok() {
                let new_sender = BALANCES.load(deps.as_ref().storage, &sender).unwrap();
                let new_recipient = BALANCES.load(deps.as_ref().storage, &recipient).unwrap();
                assert_eq!(new_sender, Uint128::new(sender_balance - transfer_amount));
                assert_eq!(new_recipient, Uint128::new(transfer_amount));
            } else {
                let new_sender = BALANCES.load(deps.as_ref().storage, &sender).unwrap();
                assert_eq!(new_sender, Uint128::new(sender_balance));
            }
        }
    }
}

6.4 Gas 分析

# 1. 使用 cosmwasm-profiler
cargo install cosmwasm-profiler
cosmwasm-profiler analyze ./artifacts/my_contract.wasm

# 2. 测试中测量 gas
#[test]
fn measure_gas_costs() {
    let mut deps = mock_dependencies();
    let env = mock_env();
    let info = mock_info("sender", &coins(1000, "umsg"));

    let start_gas = deps.as_ref().api.get_gas_used();
    let _ = execute(deps.as_mut(), env.clone(), info, ExecuteMsg::SomeHeavyOp {});
    let gas_used = deps.as_ref().api.get_gas_used() - start_gas;
    println!("Gas used: {}", gas_used);
    assert!(gas_used < 500_000, "Gas usage exceeds limit");
}

6.5 自动化审计脚本

#!/bin/bash
# audit.sh — MSG Chain 合约安全审计自动化

set -euo pipefail

CONTRACT_DIR=$1
REPORT_DIR="./audit_reports"
mkdir -p "$REPORT_DIR"

echo "=== MSG Chain 合约安全审计 ==="

# 1. 依赖审计
echo "1. 依赖审计..."
cargo audit --json > "$REPORT_DIR/dependency_audit.json" || true

# 2. Clippy
echo "2. Clippy 分析..."
cargo clippy --all-targets -- -D warnings 2>&1 | tee "$REPORT_DIR/clippy_report.txt" || true

# 3. 模式扫描
echo "3. 漏洞模式扫描..."
PATTERNS=("Addr::unchecked" "\.unwrap\(\)" "checked_add" "SubMsg" "reply_on")
for pattern in "${PATTERNS[@]}"; do
    rg --count "$pattern" "$CONTRACT_DIR" --type rust || true
done > "$REPORT_DIR/pattern_analysis.txt"

# 4. WASM 检查
echo "4. WASM 分析..."
for wasm in "$CONTRACT_DIR"/target/wasm32-unknown-unknown/release/*.wasm; do
    [ -f "$wasm" ] && cosmwasm-check "$wasm" 2>&1 | tee -a "$REPORT_DIR/wasm_check.txt"
done

echo "=== 审计完成 ==="
echo "报告: $REPORT_DIR/"

7. 审计报告模板

7.1 发现项格式

## 发现项

### [严重等级] 发现项标题

**ID**: `VULN-2026-001`
**类型**: 重入攻击 / 未授权访问 / 算术错误 / 逻辑错误
**严重等级**: Critical / High / Medium / Low
**CVSS 评分**: 9.8 (Critical)
**状态**: 已发现 / 已确认 / 已修复 / 已接受

#### 描述
[漏洞的详细描述,包括上下文和影响]

#### 影响
- 攻击者可提取合约中所有资金
- 攻击者可铸造任意数量代币

#### 风险场景
1. 攻击者调用 `withdraw` 函数
2. 合约在更新余额前发送资金
3. 攻击者通过 reply handler 重新调用
4. 重复提取直到余额为 0

#### 涉及代码
```rust
// 文件: src/contract.rs:45-67
pub fn withdraw(...) {
    // 资金发送在状态更新之前
}

修复建议

// 遵循 Checks-Effects-Interactions 模式
pub fn withdraw(...) {
    // 1. 先更新状态
    // 2. 然后发送资金
}

复现步骤

  1. 部署恶意合约
  2. 存入 1000 umsg
  3. 调用 withdraw(500)
  4. 在 reply handler 中再次调用 withdraw(500)
  5. 共提取 1000 umsg,余额记录为 500

参考资料


### 7.2 严重等级分类

┌─────────────────────┬─────────────────────────────────────┐
│ 等级 │ 定义 │
├─────────────────────┼─────────────────────────────────────┤
│ 🔴 Critical (9.0-10)│ 可直接导致资金损失或合约完全失控 │
│ 🟠 High (7.0-8.9) │ 可导致部分资金损失或关键功能失效 │
│ 🟡 Medium (4.0-6.9) │ 可导致合约逻辑异常或非关键资金风险 │
│ 🟢 Low (0.1-3.9) │ 违反最佳实践,但实际利用困难 │
│ ⚪ Info (0) │ 信息性发现或建议 │
└─────────────────────┴─────────────────────────────────────┘


### 7.3 CVSS for CosmWasm

CVSS 向量示例:
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N

AV (攻击向量): N=网络 A=相邻 L=本地 P=物理
AC (复杂度): L=低 H=高
PR (权限): N=无 L=低 H=高
S (范围): U=不可变 C=可变

严重等级映射:
0.0 - 3.9 Low
4.0 - 6.9 Medium
7.0 - 8.9 High
9.0 - 10.0 Critical


### 7.4 修复跟踪

```markdown
| 发现 ID | 等级 | 描述 | 状态 | 负责人 | 预计修复 | 实际修复 | 验证人 |
|---------|------|------|------|--------|----------|----------|--------|
| VULN-001 | Critical | 重入攻击 | 🔴 待修复 | @alice | 2026-07-15 | - | @bob |
| VULN-002 | High | 缺少权限检查 | 🟡 验证中 | @bob | 2026-07-10 | 2026-07-08 | @alice |
| VULN-003 | Medium | 精度损失 | 🟢 已修复 | @charlie | 2026-07-05 | 2026-07-04 | @alice |
| VULN-004 | Low | 事件参数缺失 | ✅ 已接受 | @dave | - | - | @alice |

7.5 完整报告模板

# 安全审计报告

**项目名称**: [合约名称]
**代码版本**: [Git commit hash]
**审计日期**: 2026-07-06 至 2026-07-20
**审计团队**: [团队名称]
**审计类型**: 完整安全审计

---

## 1. 执行摘要

[1-2 段描述审计范围、关键发现和总体安全状况]

## 2. 审计范围

| 合约 | 文件路径 | 代码行数 | commit |
|------|----------|----------|--------|
| Token | contracts/token | 450 | abc123 |
| Staking | contracts/staking | 780 | abc124 |

### 审计覆盖
- ✅ 静态代码分析
- ✅ 手动代码审查
- ✅ 模糊测试 (10,000 用例)
- ✅ Gas 分析
- ✅ 依赖审计

## 3. 关键发现总结

| 严重等级 | 数量 | 已修复 | 待修复 | 已接受 |
|----------|------|--------|--------|--------|
| Critical | 2 | 2 | 0 | 0 |
| High | 5 | 3 | 1 | 1 |
| Medium | 8 | 6 | 1 | 1 |
| Low | 12 | 8 | 2 | 2 |
| **总计** | **27** | **19** | **4** | **4** |

## 4. 详细发现项

### [Critical] VULN-2026-001: [标题]
...

## 5. 代码质量

- 代码组织: ⭐⭐⭐⭐☆
- 测试覆盖: ⭐⭐⭐☆☆
- 文档质量: ⭐⭐⭐☆☆
- 错误处理: ⭐⭐⭐⭐☆

## 6. 建议

1. 短期(1-2周): 修复所有 Critical 和 High
2. 中期(1月): 增加模糊测试和集成测试
3. 长期(3月): 形式化验证 + 监控告警

8. 安全部署检查清单

8.1 部署前检查

### □ 合约代码
- [ ] 所有 `unwrap()` 被适当地错误处理替代
- [ ] 所有 `Addr::unchecked` 被 `addr_validate` 替代
- [ ] 所有算术使用 `checked_*` 操作
- [ ] 不存在未使用的导入或变量
- [ ] clippy 检查通过(零警告)
- [ ] `cargo audit` 通过

### □ 测试
- [ ] 单元测试覆盖 > 90%
- [ ] 所有边界条件被测试
- [ ] 错误路径被测试
- [ ] 集成测试覆盖主要交易流程
- [ ] Gas 基准测试已建立

### □ 存储
- [ ] 存储键使用版本化前缀
- [ ] 不存在键碰撞
- [ ] 迁移路径已定义并测试
- [ ] 存储迭代有限制

### □ 权限
- [ ] 管理员函数有权限检查
- [ ] 紧急暂停功能已实现
- [ ] 多签已配置
- [ ] Owner 地址已安全管理

### □ 外部依赖
- [ ] 所有预言机地址已白名单
- [ ] IBC 通道已验证
- [ ] 跨合约调用已验证
- [ ] `@msg-chain/sdk` 版本已验证

### □ 部署配置
- [ ] 正确的 ChainID (`msgchain-1`)
- [ ] 正确的 gas 限制
- [ ] 正确的存储限制
- [ ] 验证者抵押已确认

8.2 多签部署流程

// ✅ 多签部署模块
const REQUIRED_SIGNATURES: u32 = 3;
const MULTISIG_MEMBERS: [&str; 5] = ["alice", "bob", "charlie", "dave", "eve"];

const PENDING_DEPLOY: Item<PendingDeploy> = Item::new("pending_deploy_v1");
const APPROVALS: Map<&Addr, bool> = Map::new("approvals_v1");

pub struct PendingDeploy {
    pub code_id: u64,
    pub label: String,
    pub admin: Option<String>,
    pub msg: Binary,
    pub created_at: u64,
}

#[entry_point]
pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> StdResult<Response> {
    match msg {
        ExecuteMsg::ApproveDeploy => {
            let pending = PENDING_DEPLOY.load(deps.storage)?;
            if env.block.time.seconds() - pending.created_at > 86400 {
                return Err(StdError::generic_err("Proposal expired"));
            }
            let is_member = MULTISIG_MEMBERS.contains(&info.sender.as_str());
            if !is_member {
                return Err(StdError::generic_err("Not a multisig member"));
            }
            APPROVALS.save(deps.storage, &info.sender, &true)?;

            let approval_count = MULTISIG_MEMBERS.iter()
                .filter(|m| APPROVALS.may_load(deps.storage, &Addr::unchecked(**m))
                    .unwrap_or(Some(false)).unwrap_or(false))
                .count() as u32;

            if approval_count >= REQUIRED_SIGNATURES {
                Ok(Response::new().add_attribute("action", "deploy_approved"))
            } else {
                Ok(Response::new().add_attribute("action", "approval_added")
                    .add_attribute("approvals", approval_count.to_string()))
            }
        }
        // ...
    }
}

8.3 紧急暂停/升级

// ✅ 紧急暂停机制
#[derive(Serialize, Deserialize)]
pub struct EmergencyState {
    pub paused: bool,
    pub paused_by: Option<Addr>,
    pub paused_at: Option<u64>,
    pub pause_reason: Option<String>,
}

const EMERGENCY: Item<EmergencyState> = Item::new("emergency_v1");

pub fn assert_not_paused(deps: &Deps) -> StdResult<()> {
    let emergency = EMERGENCY.load(deps.storage)?;
    if emergency.paused {
        return Err(StdError::generic_err("Contract is paused"));
    }
    Ok(())
}

#[entry_point]
pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> StdResult<Response> {
    assert_not_paused(&deps.as_ref())?;
    match msg {
        ExecuteMsg::EmergencyPause { reason } => {
            let config = CONFIG.load(deps.storage)?;
            if info.sender != config.admin {
                return Err(StdError::generic_err("Unauthorized"));
            }
            EMERGENCY.save(deps.storage, &EmergencyState {
                paused: true,
                paused_by: Some(info.sender),
                paused_at: Some(env.block.time.seconds()),
                pause_reason: Some(reason),
            })?;
            Ok(Response::new().add_attribute("action", "emergency_pause"))
        }
        ExecuteMsg::EmergencyUnpause => {
            let config = CONFIG.load(deps.storage)?;
            if info.sender != config.admin {
                return Err(StdError::generic_err("Unauthorized"));
            }
            EMERGENCY.save(deps.storage, &EmergencyState {
                paused: false, paused_by: None,
                paused_at: None, pause_reason: None,
            })?;
            Ok(Response::new().add_attribute("action", "emergency_unpause"))
        }
        // 其他消息
    }
}

8.4 事故响应计划

## 事故响应计划

### 检测阶段
1. 监控合约异常交易(大额转账、频繁调用)
2. 监控验证者集异常变化
3. 监控 Agent 异常行为
4. 监控合约暂停状态
5. 监控链上事件/属性异常

### 遏制阶段
1. **暂停合约**: 调用 `EmergencyPause`
2. **限制 Agent**: 更新 Agent 宪法为只读
3. **通知验证者**: 通过多签通道通知
4. **收集证据**: 记录异常交易 hash
5. **隔离资金**: 必要时迁移至安全合约

### 恢复阶段
1. **分析根因**: 确定漏洞类型和触发条件
2. **开发修复**: 编写并通过测试
3. **测试修复**: 在本地测试网完整测试
4. **治理投票**: 提交升级提案给验证者
5. **部署修复**: 通过多签部署
6. **恢复资金**: 评估恢复方案
7. **取消暂停**: 验证修复后解暂停

### 事后总结
1. 编写事故报告
2. 根本原因分析
3. 更新安全流程
4. 添加新监控指标
5. 团队复盘

9. 事故响应

9.1 检测方法

// ✅ 合约级异常检测
pub fn detect_anomalies(
    deps: DepsMut,
    env: Env,
    _info: MessageInfo,
) -> StdResult<Response> {
    let config = MONITOR_CONFIG.load(deps.storage)?;
    let mut alerts = Vec::new();

    // 1. 检测大额转账
    let recent_transfers: Vec<_> = RECENT_TRANSFERS
        .range(deps.storage, None, None, Order::Descending)
        .take(100).collect();

    let total_volume: Uint128 = recent_transfers.iter()
        .map(|(_, t)| t.amount).sum();

    if total_volume > config.daily_volume_threshold {
        alerts.push(format!("High volume: {}", total_volume));
    }

    // 2. 检测频繁调用
    let call_count = CALL_FREQUENCY
        .may_load(deps.storage, &(env.block.time.seconds() / 3600))?
        .unwrap_or(0);

    if call_count > config.max_calls_per_hour {
        alerts.push(format!("Rate limit: {} calls/hour", call_count));
    }

    // 3. 检测异常验证者活动
    let validator_changes = VALIDATOR_CHANGES
        .may_load(deps.storage)?.unwrap_or(0);
    if validator_changes > config.max_validator_changes {
        alerts.push("Abnormal validator changes".to_string());
    }

    if !alerts.is_empty() {
        let alert = SecurityAlert {
            alerts: alerts.clone(),
            timestamp: env.block.time.seconds(),
            block_height: env.block.height,
        };
        SECURITY_ALERTS.save(deps.storage, &env.block.height, &alert)?;
    }

    Ok(Response::new()
        .add_attribute("action", "anomaly_detection")
        .add_attribute("alerts", alerts.len().to_string()))
}

9.2 遏制流程

// ✅ 遏制操作 — 暂停所有非关键操作
#[entry_point]
pub fn emergency_containment(
    deps: DepsMut,
    env: Env,
    _info: MessageInfo,
) -> StdResult<Response> {
    // 停止所有转账
    EMERGENCY.save(deps.storage, &EmergencyState {
        paused: true,
        paused_by: Some(Addr::unchecked("system")),
        paused_at: Some(env.block.time.seconds()),
        pause_reason: Some("Security incident".to_string()),
    })?;

    // 锁定所有 Agent
    let agents: Vec<Addr> = ALL_AGENTS
        .keys(deps.storage, None, None, Order::Ascending)
        .collect();

    for agent_id in agents {
        AGENTS.update(deps.storage, &agent_id, |agent| -> StdResult<_> {
            let mut a = agent.unwrap();
            a.paused = true;
            a.constitution = Some(Constitution::read_only());
            Ok(a)
        })?;
    }

    Ok(Response::new()
        .add_attribute("action", "emergency_containment")
        .add_attribute("agents_locked", agents.len().to_string()))
}

// 资金快照
pub fn snapshot_funds(deps: DepsMut, env: Env) -> StdResult<Response> {
    let snapshot = FundsSnapshot {
        block_height: env.block.height,
        timestamp: env.block.time.seconds(),
        contract_balance: deps.querier.query_all_balances(&env.contract.address)?,
    };
    SNAPSHOTS.save(deps.storage, &env.block.height, &snapshot)?;
    Ok(Response::new().add_attribute("action", "funds_snapshotted"))
}

9.3 事后分析模板

# 安全事件事后分析

**事件日期**: 2026-07-06
**报告日期**: 2026-07-07
**报告人**: [姓名]
**严重等级**: [P0/P1/P2]

## 概述

[1-2 段描述事件概要]

## 时间线

| 时间 (UTC) | 事件 |
|------------|------|
| 14:23:15 | 攻击者合约部署 |
| 14:23:45 | 第一次异常交易 |
| 14:24:00 | 检测到异常活动 |
| 14:24:15 | 触发 EmergencyPause |
| 14:25:00 | 验证者收到通知 |
| 15:00:00 | 漏洞分析完成 |
| 16:00:00 | 修复补丁部署 |

## 根本原因

[描述漏洞的技术原因]

## 影响

- 受影响用户: [数量]
- 资金损失: [金额]
- 波及合约: [列表]

## 修复措施

1. [修复 1]
2. [修复 2]

## 后续改进

1. [改进 1]
2. [改进 2]

## 行动项

| 行动项 | 负责人 | 截止日期 | 状态 |
|--------|--------|----------|------|
| [行动] | @name | 2026-07-13 | 进行中 |

## 附件

- 交易哈希: [tx_hash]
- 攻击合约地址: [address]
- 相关代码 commit: [commit]

MSG Chain Whitepaper | https://msgchain.org/whitepaper | 代码库实际状态,不代表生产可用