AI Agent 合约模板与快速代码生成指南
基于 MSG Chain 白皮书真实数据构建
文档版本: v1 · schema_version: v1
链 ID:msg-chain-1⚠️ No-Go Disclaimer: MSGChain 主网裁决为 No-Go。本文件所有内容反映的是开发阶段的技术设计,不代表主网未独立核验上线状态。生产部署状态请以白皮书为准:https://msgchain.org/whitepaper/
目录
1. 概述
1.1 本指南的定位
本文档面向 AI Agent(大语言模型驱动的编码代理),系统说明如何利用 MSG Chain
白皮书中公开的 contract_templates/ 和 recipes/ 两大机器可消费资源,
快速生成 CosmWasm 合约代码、测试骨架和部署计划。
1.2 资源结构
MSG Chain 白皮书在 https://msgchain.org/whitepaper/ 下公开了三层机器可读资源:
| 资源 | 路径 | 用途 |
|---|---|---|
| 模板索引 | contract_templates/index.json |
发现可用模板元信息 |
| 模板包 | contract_templates/counter_v1/ |
实际代码文件(Cargo.toml, msg.rs, state.rs, contract.rs, tests/) |
| 配方 | recipes/contract_minimal.json |
定义 AI coding 全流程的步骤、输入、产出物 |
| 模块导出 | module_exports/contract.json |
合约运行时模块的机器摘要 |
| 模块导出 | module_exports/registry.json |
创世注册中心模块的机器摘要 |
| 模块导出 | module_exports/rpc.json |
RPC/API 接口模块的机器摘要 |
| 能力矩阵 | developer_capability_matrix.json |
每个开发表面的机器就绪度评分 |
1.3 Machine Readiness 体系
developer_capability_matrix.json 以 machine_readiness 字段标注每个表面的就绪等级。
对合约开发最相关的两个表面:
contract_template_pack — machine_readiness: starter_ready, production_supported: false
best_for:
- 快速生成最小可运行合约
- 补充消息 schema
- 生成 cargo test 骨架
blocking_gaps:
- 模板仍是 starter,不覆盖所有业务模式
- 没有替代业务审计与测试
boundaries:
- 模板包用于加速 AI coding,不等于官方系统合约或经过审计的业务模板。
contract_runtime — machine_readiness: assisted_codegen, production_supported: true
best_for:
- 合约消息设计
- 状态流转设计
- 部署调用路径生成
- 回执验证规划
source_modules:
- contract.html (已实现)
- registry.html (已实现)
- rpc.html (已实现)
1.4 何时使用模板 vs 配方
| 场景 | 使用 |
|---|---|
| 需要立即写出一个可编译的合约 | 模板 — counter_v1 起步 |
| 需要理解完整的 AI coding 工作流 | 配方 — contract_minimal 的 7 步流程 |
| 需要设计消息结构和状态模型 | 模板 + 运行时模块导出 |
| 需要生成测试骨架 | 模板的 integration.rs 为起点 |
| 需要部署计划和回执验证 | 配方的 step 5-7 |
| 需要判断能否生产上线 | 能力矩阵的 blocking_gaps + boundaries |
1.5 数据来源说明
本文档所有引用均来自 MSG Chain 白皮书公开文件,具体来源:
contract_templates/index.json— 模板索引contract_templates/counter_v1/manifest.json— counter_v1 模板清单contract_templates/counter_v1/Cargo.toml— 依赖配置contract_templates/counter_v1/src/msg.rs— 消息定义contract_templates/counter_v1/src/state.rs— 状态定义contract_templates/counter_v1/src/contract.rs— 合约入口contract_templates/counter_v1/tests/integration.rs— 测试骨架recipes/contract_minimal.json— 最小合约配方developer_capability_matrix.json— 能力矩阵module_exports/contract.json— 合约模块导出module_exports/registry.json— 注册中心模块导出module_exports/rpc.json— RPC 模块导出
2. 合约模板索引
2.1 索引结构
contract_templates/index.json 是模板的发现入口,其 schema 为:
{
"schema_version": "v1",
"generated_by": "msg_whitepaper_pipeline_v1",
"templates": [
{
"id": "counter_v1",
"path": "contract_templates/counter_v1/manifest.json",
"public_url": "https://msgchain.org/whitepaper/contract_templates/counter_v1/manifest.json",
"description": "Minimal CosmWasm starter template for AI-assisted contract coding on MSG."
}
],
"reference_sources": [
"contracts/cosmwasm/all/genesis_registry_v1/src/msg.rs",
"contracts/cosmwasm/all/agent_registry_v1/src/msg.rs"
],
"metadata_profile": "public_stable"
}
当前索引公开一个模板 counter_v1,类型为 starter。
reference_sources 列出两个可作为消息设计参考的真实合约源文件。
2.2 AI Agent 读取索引
import requests
TEMPLATES_INDEX = 'https://msgchain.org/whitepaper/contract_templates/index.json'
async def list_templates():
index = await requests.get(TEMPLATES_INDEX).json()
return index.get('templates', [])
async def resolve_template(template_id: str):
index = await requests.get(TEMPLATES_INDEX).json()
for t in index.get('templates', []):
if t['id'] == template_id:
manifest = await requests.get(t['public_url']).json()
return manifest
return None
2.3 模板清单结构
每个模板通过 manifest.json 描述自身:
{
"schema_version": "v1",
"generated_by": "msg_whitepaper_pipeline_v1",
"template_id": "counter_v1",
"template_kind": "starter",
"public_url": "https://msgchain.org/whitepaper/contract_templates/counter_v1/manifest.json",
"entry_files": [
"contract_templates/counter_v1/Cargo.toml",
"contract_templates/counter_v1/src/msg.rs",
"contract_templates/counter_v1/src/state.rs",
"contract_templates/counter_v1/src/contract.rs",
"contract_templates/counter_v1/tests/integration.rs"
],
"notices": [
"该模板用于 AI coding 起步,不是官方系统合约。",
"部署前必须按业务需求、权限模型、Gas 与安全审计进行重写。"
],
"source_modules": ["contract.html", "registry.html", "rpc.html"],
"metadata_profile": "public_stable"
}
entry_files 告诉 AI Agent 哪些文件需要被读取或生成本地副本。
notices 是必须传递给开发者的边界声明。
source_modules 关联的文档模块包含运行时上下文。
2.4 文件清单说明
counter_v1 模板包含 5 个文件:
| 文件 | 用途 | 是否必须修改 |
|---|---|---|
Cargo.toml |
依赖声明,适配 CosmWasm 1.5 | 通常保留,按需加依赖 |
src/msg.rs |
InstantiateMsg / ExecuteMsg / QueryMsg 定义 | 核心修改点 |
src/state.rs |
状态结构与存储键 | 核心修改点 |
src/contract.rs |
instantiate / execute / query 入口 | 核心修改点 |
tests/integration.rs |
集成测试骨架 | 必须扩展 |
2.5 模板文件详解
Cargo.toml
[package]
name = "msg-counter-v1"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
cosmwasm-schema = "1.5.0"
cosmwasm-std = "1.5.0"
cw-storage-plus = "1.2.0"
serde = { version = "1.0", default-features = false, features = ["derive"] }
thiserror = "1.0"
注意:
crate-type包含["cdylib", "rlib"],这是 CosmWasm 合约的标准配置cosmwasm-schema用于生成 JSON Schemacw-storage-plus提供Item和Map等存储原语
src/msg.rs — 消息定义
use cosmwasm_schema::{cw_serde, QueryResponses};
#[cw_serde]
pub struct InstantiateMsg {
pub owner: Option<String>,
pub count: i32,
}
#[cw_serde]
pub enum ExecuteMsg {
Increment {},
Reset { count: i32 },
}
#[cw_serde]
#[derive(QueryResponses)]
pub enum QueryMsg {
#[returns(CountResponse)]
GetCount {},
}
#[cw_serde]
pub struct CountResponse {
pub count: i32,
pub owner: String,
}
这是 AI Agent 需要优先修改的文件。模板提供了一个"计数增加"的简单业务:
InstantiateMsg在部署时初始化计数和所有者ExecuteMsg定义写操作(增量、重置)QueryMsg定义读操作#[returns(...)]为每个 Query 变体标注响应类型
src/state.rs — 状态定义
use cosmwasm_schema::cw_serde;
use cw_storage_plus::Item;
#[cw_serde]
pub struct State {
pub count: i32,
pub owner: String,
}
pub const STATE: Item<State> = Item::new("state");
使用 cw_storage_plus::Item 作为单一存储槽。
Item::new("state") 中的字符串是存储键。
src/contract.rs — 合约入口
use cosmwasm_std::{
entry_point, to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdError,
StdResult,
};
use crate::msg::{CountResponse, ExecuteMsg, InstantiateMsg, QueryMsg};
use crate::state::{State, STATE};
#[entry_point]
pub fn instantiate(
deps: DepsMut,
_env: Env,
info: MessageInfo,
msg: InstantiateMsg,
) -> StdResult<Response> {
let owner = msg.owner.unwrap_or_else(|| info.sender.to_string());
let state = State { count: msg.count, owner: owner.clone() };
STATE.save(deps.storage, &state)?;
Ok(Response::new()
.add_attribute("action", "instantiate")
.add_attribute("owner", owner))
}
#[entry_point]
pub fn execute(
deps: DepsMut,
_env: Env,
info: MessageInfo,
msg: ExecuteMsg,
) -> StdResult<Response> {
match msg {
ExecuteMsg::Increment {} => {
STATE.update(deps.storage, |mut state| -> StdResult<_> {
if info.sender.to_string() != state.owner {
return Err(StdError::generic_err("only owner can increment"));
}
state.count += 1;
Ok(state)
})?;
Ok(Response::new().add_attribute("action", "increment"))
}
ExecuteMsg::Reset { count } => {
STATE.update(deps.storage, |mut state| -> StdResult<_> {
if info.sender.to_string() != state.owner {
return Err(StdError::generic_err("only owner can reset"));
}
state.count = count;
Ok(state)
})?;
Ok(Response::new()
.add_attribute("action", "reset")
.add_attribute("count", count.to_string()))
}
}
}
#[entry_point]
pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
match msg {
QueryMsg::GetCount {} => {
let state = STATE.load(deps.storage)?;
to_json_binary(&CountResponse {
count: state.count,
owner: state.owner,
})
}
}
}
三个 #[entry_point] 函数:
instantiate— 合约初始化,保存状态execute— 处理写操作,含权限检查query— 处理读操作
tests/integration.rs — 测试骨架
#[test]
fn ai_agent_should_customize_business_rules_before_deploy() {
let goal = "replace owner-only counter logic with actual product logic";
assert!(goal.contains("product logic"));
}
这是有意设计的"反例测试"——它不测试计数器逻辑,而是提醒 AI Agent:
这个模板的测试需要被真实的业务测试替换。
2.6 模板分类
当前的 template_kind: "starter" 表示:
starter: 提供最小可运行骨架,适合 AI coding 起步
planned: 规划中,尚未公开
production: 经过审计的开发参考级模板(当前未提供)
2.7 参考源文件
索引中的 reference_sources 指向两个真实合约的消息定义文件:
genesis_registry_v1/src/msg.rs— 创世注册中心的消息 schemaagent_registry_v1/src/msg.rs— Agent 注册中心的消息 schema
AI Agent 在设计自己的消息结构前,应该读取这些文件了解 MSG Chain 的消息设计惯例。
2.8 获取模板的 Python 函数
import requests
import os
TEMPLATE_BASE = 'https://msgchain.org/whitepaper/contract_templates/counter_v1'
FILES = ['Cargo.toml', 'src/msg.rs', 'src/state.rs', 'src/contract.rs', 'tests/integration.rs']
def fetch_counter_template(target_dir: str):
os.makedirs(f'{target_dir}/src', exist_ok=True)
os.makedirs(f'{target_dir}/tests', exist_ok=True)
for f in FILES:
url = f'{TEMPLATE_BASE}/{f}'
content = requests.get(url).text
with open(f'{target_dir}/{f}', 'w') as fh:
fh.write(content)
print(f'Template written to {target_dir}')
3. 最小合约配方
3.1 配方结构
recipes/contract_minimal.json 是整个白皮书中面向 AI Agent 最核心的资源。
它定义了一条从需求输入到部署计划输出的完整闭环。
配方顶层结构:
{
"schema_version": "v1",
"recipe_id": "contract_minimal",
"title": "MSG 合约 AI Coding 最小闭环",
"goal": "Guide an AI coding agent from requirement intake to contract code generation, testing, deployment planning, receipt verification, and guarded release review.",
"entry_point": "developer_entry.json",
"required_modules": [...],
"recommended_chunks": [...],
"required_human_inputs": [...],
"workflow": [...],
"success_artifacts": [...],
"hard_boundaries": [...],
"metadata_profile": "public_stable"
}
3.2 AI Agent 读取配方
RECIPE_URL = 'https://msgchain.org/whitepaper/recipes/contract_minimal.json'
async def load_recipe():
recipe = await requests.get(RECIPE_URL).json()
return recipe
3.3 必需模块
配方要求 AI Agent 先读取三个已实现的模块:
| 模块 | 文件 | 状态 |
|---|---|---|
| 智能合约引擎 (CosmWasm) | contract.html |
已实现 |
| 创世注册中心 (genesis_registry_v1) | registry.html |
已实现 |
| RPC 与 API 接口 | rpc.html |
已实现 |
这些模块的机器摘要通过 module_exports/ 下的 JSON 文件提供:
module_exports/contract.jsonmodule_exports/registry.jsonmodule_exports/rpc.json
3.4 推荐阅读的 Chunk
配方建议 AI Agent 读取以下 chunks 建立链路理解:
| chunk_id | 内容摘要 |
|---|---|
contract#1 |
合约执行边界、genesis registry v1 canonical key、wasm migrate/reply on/atomic rollback |
contract#2 |
系统合约拓扑:Token → DAO → Gas → Emission → Candidate → Validator |
registry#1 |
Canonical key 空间、active/reserved、分页查询 |
registry#2 |
分页列表与状态模型 |
rpc#1 |
共享 Mux 与多协议端点 |
rpc#2 |
认证与 API 族 |
3.5 必需的人类输入
配方明确列出 AI Agent 必须从开发者处获取的信息:
1. 业务目标与状态模型
2. 管理员 / 多签 / 治理控制要求
3. 部署网络与目标地址策略
4. 真实签名账户、Gas 预算与发布窗口
缺少这些输入时,AI Agent 不应尝试生成最终代码或部署计划。
3.6 7 步工作流
这是配方的核心——定义 AI Agent 从需求到部署回执的完整路径:
Step 1: 读取入口 + 能力矩阵
action: read developer_entry + capability matrix
purpose: 识别哪些面适合自动生成、哪些仍是边界面。
AI Agent 应首先理解自己的能力边界。developer_capability_matrix.json
给出了每个开发表面的 machine_readiness,告诉 AI 哪些可以写代码,
哪些只能做参考,哪些还不能碰。
Step 2: 读取模块与 Chunk
action: read contract/rpc/registry modules and chunks
purpose: 建立 Instantiate / Execute / Query / wasm_migrate / canonical key 的链路理解。
从 module_exports/ 获取模块摘要,从 module_chunks/ 获取详细证据。
目标是理解:
- CosmWasm 运行时如何执行合约
- Registry 如何解析 canonical key 到合约地址
- RPC 如何广播交易和查询状态
Step 3: 生成合约消息与状态设计
action: generate contract message and state design
purpose: 输出 Rust 合约骨架、消息定义、状态结构与权限模型。
基于模板(counter_v1)的业务逻辑替换:
src/msg.rs— 改为目标业务的消息结构src/state.rs— 改为目标业务的状态模型src/contract.rs— 实现业务逻辑和权限
Step 4: 生成测试与失败场景
action: generate cargo tests and failure cases
purpose: 覆盖 happy path、权限拒绝、回滚、reply/submessage 等关键路径。
替换模板中的反例测试,生成:
- Happy path 测试
- 权限拒绝测试(非 owner 调用)
- 无效输入测试
- 可选:reply / submessage 测试
Step 5: 准备部署计划
action: prepare deploy plan
purpose: 形成 StoreCode / Instantiate / Query / Execute / Receipt 校验计划,
而不是只写代码。
部署计划应包括:
- 编译合约:
cargo wasm - 优化 wasm:
cargo run-script optimize - StoreCode 交易:广播 wasm 字节码
- Instantiate 交易:初始化合约状态
- Query 验证:确认状态正确
- Execute 测试:执行写操作
- Receipt 校验:解析交易回执
Step 6: 收集交易证据
action: collect tx hash, receipt, query replay, evidence refs
purpose: 把部署结果回写到可验证证据链。
部署后的证据收集:
- tx hash
- receipt JSON
- query replay 响应
- 证据引用(evidence refs)
Step 7: 人类上线门禁
action: human release gate
purpose: 由人类确认密钥、治理条件、上线窗口与生产风险。
AI Agent 不能独立上生产。必须经过人类确认:
- 私钥与签名账户安全
- 治理条件满足
- 上线窗口合适
- 生产风险可接受
3.7 成功产出物
按配方执行完成后,AI Agent 应交付:
1. 合约源码目录
2. cargo test 输出
3. 部署与调用脚本
4. tx hash / receipt / query replay
5. 上线前边界说明
3.8 硬边界
配方的 hard_boundaries 是 AI Agent 必须遵守的限制:
1. 当前 starter template pack 只提供起步骨架,不等于业务合约已自动完成。
2. 没有真实钱包、私钥、部署权限与 Gas 预算,AI 不能独立完成最终上链。
3. 高价值或治理相关动作必须经过人类审批与门禁验证。
3.9 Python 配方引擎实现
import requests
import json
import os
from typing import Optional
RECIPE_URL = 'https://msgchain.org/whitepaper/recipes/contract_minimal.json'
COUNTER_MANIFEST_URL = 'https://msgchain.org/whitepaper/contract_templates/counter_v1/manifest.json'
async def scaffold_contract(name: str, template: str = 'counter_v1') -> dict:
recipe = await requests.get(RECIPE_URL).json()
manifest = await requests.get(COUNTER_MANIFEST_URL).json()
template_files = {}
for filepath in manifest['entry_files']:
url = f'https://msgchain.org/whitepaper/{filepath}'
template_files[filepath] = await requests.get(url).text()
return {
'recipe': recipe['recipe_id'],
'workflow': recipe['workflow'],
'required_human_inputs': recipe['required_human_inputs'],
'hard_boundaries': recipe['hard_boundaries'],
'template_files': template_files,
'notices': manifest['notices'],
}
def write_scaffold(target_dir: str, scaffold: dict):
for filepath, content in scaffold['template_files'].items():
relpath = filepath.replace('contract_templates/counter_v1/', '')
fullpath = os.path.join(target_dir, relpath)
os.makedirs(os.path.dirname(fullpath), exist_ok=True)
with open(fullpath, 'w') as f:
f.write(content)
notices_path = os.path.join(target_dir, 'NOTICES.md')
with open(notices_path, 'w') as f:
for notice in scaffold['notices']:
f.write(f'- {notice}\n')
print(f'[scaffold] {len(scaffold["template_files"])} files written to {target_dir}')
print(f'[scaffold] workflow: {len(scaffold["workflow"])} steps')
print(f'[scaffold] required inputs: {scaffold["required_human_inputs"]}')
4. 合约运行时与生命周期
4.1 模块总览
合约运行时涉及三个已实现的模块,它们构成了 AI Agent 理解 MSG Chain
合约执行时必须掌握的知识基底。
| 模块 | 模块导出 | 状态 |
|---|---|---|
| 智能合约引擎 (CosmWasm) | module_exports/contract.json |
已实现 |
| 创世注册中心 | module_exports/registry.json |
已实现 |
| RPC 与 API 接口 | module_exports/rpc.json |
已实现 |
4.2 contract.html — 智能合约引擎
模块导出 contract.json 的关键信息:
{
"title": "智能合约引擎 (CosmWasm)",
"status": "implemented",
"group": "control",
"tags": ["金库", "多签", "阈值", "治理", "时间锁", "提案", "注册中心", "规范键"],
"outlinks": ["registry.html"],
"code_refs": [
"pkg/wasm/core_contracts_e2e_test.go",
"pkg/quantum/node.go",
"pkg/wasm/real_vm.go",
"pkg/quantum/transaction_executor.go",
"pkg/quantum/tendermint_rpc.go"
]
}
执行时主路径
合约执行流程:
Tx (StoreCode / Instantiate / Execute / Query / wasm_migrate)
→ CosmWasm VM 沙箱
→ GasMeter / Env / Context / State Access
→ atomic root / child checkpoint rollback
→ Submessage / reply on / CosmWasm reply
→ Badger 状态 / WasmInstance 映射 / Contract KV
→ 回执 / 事件 / failed receipt
→ 成功 → Quantum Node 合约集成器
→ 失败 → 合约错误 / Gas / Query 异常 → fail-closed / 失败回执
核心系统合约拓扑
当前已部署的系统级合约及其依赖关系:
msg token cw20
→ dar rating v1
→ dao governance v1
→ gas fee distribution v2
→ emission schedule v2
block time schedule v1 → emission schedule v2
gas fee distribution v2 → candidate node staking v2
candidate node staking v2 → validator qualification v2
dar rating v1 → foundation treasury v2
dao governance v1 → foundation treasury v2
genesis registry v1
→ foundation treasury v2
→ dao governance v1
→ gas fee distribution v2
→ emission schedule v2
→ block time schedule v1
→ candidate node staking v2
→ validator qualification v2
Canonical Key 空间
dao governance → genesis registry v1
foundation treasury → genesis registry v1
gas fee distribution → genesis registry v1
economic constitution → genesis registry v1 (reserved)
block time schedule → genesis registry v1
emission schedule → genesis registry v1
运行时边界
- 升级走链上
wasm_migrate交易,留下 receipt / event reply on = success/error/always/never与reply已进入当前本地可证边界- Native BankMsg / Gov / Staking / IBC / Stargate / Any 仍未放开,必须标成 fail-closed
unsupported CosmosMsg继续 fail-closed,不伪装成"已支持"- 生产升级入口改为链上 wasm migrate tx;
/api/v1/contracts/rebind生产禁用(HTTP 410)
合约寻址(contract + registry 分界)
contract.html讲的是 VM 运行时、系统合约协作、回执与节点执行面registry.html讲的是 canonical key、分页查询、active/reserved、地址解析闭环- 节点运行时优先按 canonical role 解析地址,具体 v1/v2 名称只允许保留在 alias / genesis / artifact / test 边界
4.3 registry.html — 创世注册中心
模块导出 registry.json 的关键信息:
{
"title": "创世注册中心 (genesis_registry_v1)",
"status": "implemented",
"group": "control",
"tags": ["金库", "多签", "阈值", "治理", "时间锁", "提案", "注册中心", "规范键"],
"outlinks": ["dao.html", "foundation.html", "emission.html"],
"code_refs": [
"pkg/quantum/node.go",
"pkg/quantum/contract_integrator_v1.go"
]
}
Canonical Key 查询
get contract by canonical key(gas fee distribution) =>
{
"canonical key": "gas fee distribution",
"status": "active",
"contract name": "gas fee distribution v2",
"contract address": "msg1gasv20000000000000000000000000000000000",
"updated at height": 12345,
"updated at time": "redacted"
}
get contract by canonical key(economic constitution) =>
{
"canonical key": "economic constitution",
"status": "reserved",
"contract name": null,
"contract address": null,
"updated at height": 12345,
"updated at time": "redacted"
}
关键语义:
active— 已部署且可解析地址reserved— 未部署 / 保留键位,不伪装成 active
分页查询
list contracts(limit=5) =>
{
"contracts": [
{"contract name": "block time schedule v1"},
{"contract name": "msg token cw20"},
{"contract name": "candidate node staking v2"},
{"contract name": "emission schedule v2"},
{"contract name": "challenge slashing v1"}
],
"total contracts": 12
}
list contracts(limit=5, start after="challenge slashing v1") =>
{
"contracts": [
{"contract name": "dao governance v1"},
{"contract name": "dar rating v1"},
{"contract name": "emission schedule v2"},
{"contract name": "foundation treasury v2"},
{"contract name": "gas fee distribution v2"}
],
"total contracts": 12
}
节点运行时解析流程
Node 启动 → delayed refresh
→ normalizeContractRoleAliases(addresses, canonicalRole, aliases...)
→ contractAddressForRole(canonical role)
→ 成功: 写入 merged contractAddresses
→ 失败: 记录日志并继续,不伪装成已解析
4.4 rpc.html — RPC 与 API 接口
模块导出 rpc.json 的关键信息:
{
"title": "RPC 与 API 接口",
"status": "implemented",
"group": "runtime",
"tags": ["铸造", "奖励", "结算", "接口", "查询", "P2P", "组网", "NAT"],
"code_refs": [
"pkg/quantum/tendermint_rpc.go"
]
}
支持端点族
| 端点族 | 方法 |
|---|---|
| Tendermint RPC | broadcast_tx_commit, broadcast_tx_sync, broadcast_tx_async, abci_query, tx, block, blockchain, net_info |
| Explorer | /api/v1/blocks/latest, /api/v1/blocks, /api/v1/txs, /api/v1/receipts, /api/v1/address |
| Contracts | /api/v1/contracts/deploy, /api/v1/contracts/instantiate, /api/v1/contracts/execute |
| Server-side signing | /api/v1/tx/transfer, /api/v1/tx/delegate, /api/v1/tx/undelegate, /api/v1/tx/send |
| Cosmos REST | /api/v1/bank/balances, /api/v1/staking/validators, /api/v1/auth/accounts |
| Tunnel status | /api/v1/status → libp2p peer id / libp2p addrs |
部署推荐
- P2P / RPC / API 三主口分离
shared mux是兼容能力,不再表述为现行默认标准- 同机双节点时,端口切换需联动 machine.env、监控、tunnel 与采证脚本
4.5 合约代码生成与运行时对接
AI Agent 生成的合约代码需要与运行时正确对接:
StoreCode
import requests
import json
RPC_ENDPOINT = 'https://rpc.msgchain.org'
def build_store_code_tx(wasm_bytecode: bytes, sender: str) -> dict:
return {
'jsonrpc': '2.0',
'method': 'broadcast_tx_commit',
'params': {
'tx': {
'type': 'wasm/StoreCode',
'value': {
'sender': sender,
'wasm_byte_code': wasm_bytecode.hex(),
}
}
},
'id': 1
}
Instantiate
def build_instantiate_tx(code_id: int, msg: dict, sender: str, label: str, admin: str) -> dict:
return {
'jsonrpc': '2.0',
'method': 'broadcast_tx_commit',
'params': {
'tx': {
'type': 'wasm/Instantiate',
'value': {
'sender': sender,
'code_id': str(code_id),
'msg': msg,
'label': label,
'admin': admin,
'funds': []
}
}
},
'id': 1
}
Execute
def build_execute_tx(contract_addr: str, msg: dict, sender: str) -> dict:
return {
'jsonrpc': '2.0',
'method': 'broadcast_tx_commit',
'params': {
'tx': {
'type': 'wasm/Execute',
'value': {
'sender': sender,
'contract': contract_addr,
'msg': msg,
'funds': []
}
}
},
'id': 1
}
Query
def build_query_request(contract_addr: str, msg: dict) -> dict:
return {
'jsonrpc': '2.0',
'method': 'abci_query',
'params': {
'path': f'/wasm/contract/{contract_addr}',
'data': msg,
'height': '0',
'prove': False
},
'id': 1
}
4.6 AI Agent 理解运行时语义
AI Agent 在生成合约代码时,必须理解以下运行时语义:
StoreCode 阶段:
- 部署 wasm 字节码到链上,返回 code_id
- 需要 Gas 预算
- 需要签名账户
Instantiate 阶段:
- 基于 code_id 创建合约实例
InstantiateMsg由 AI Agent 设计- 可指定 admin(用于后续 migrate)
- 返回合约地址
Execute 阶段:
- 调用合约的
ExecuteMsg - 携带
MessageInfo(sender, funds) - 合约内可做权限检查
- 支持 submessage 和 reply
Query 阶段:
- 调用合约的
QueryMsg - 只读,不修改状态
- 不需要签名
Migrate 阶段:
- 由 admin 账户发起
- 更新合约代码到新的 code_id
- 走链上
wasm_migrate交易
4.7 生命周期边界
production_supported: true (contract_runtime)
合约运行时本身是生产就绪的,但 AI 生成的合约代码(基于 starter 模板)
需要经过业务逻辑审查和测试才能上线。
5. 代码生成最佳实践
5.1 理解 machine_readiness 等级
AI Agent 在生成代码前,必须确认自己正在操作的 surface 的 readiness 等级:
starter_ready → 可以起步,但不能直接用于生产
assisted_codegen → AI 辅助生成,但需要人类确认
source_backed_reference → 只能做参考,不能直接生成
guarded_integration → 集成有风险,需要谨慎
guarded_write → 写路径受保护,AI 不能独立执行
read_only_assist → 只读辅助
fail_closed_reference → 默认不可用
local_candidate → 仅本地候选
5.2 模板使用三原则
原则一:模板是起点,不是终点
counter_v1 starter 模板提供的是骨架:
- 一个简单的计数逻辑
- owner 权限检查
- 一个测试文件(内容为提醒而非真实测试)
必须替换为实际业务逻辑。
原则二:不要保留模板的"反例测试"
原测试文件内容:
#[test]
fn ai_agent_should_customize_business_rules_before_deploy() {
let goal = "replace owner-only counter logic with actual product logic";
assert!(goal.contains("product logic"));
}
这个测试不是真正的测试——它是 AI Agent 的指令。
AI Agent 必须将其替换为真实的业务测试。
原则三:必须传递 notices
模板 manifest.json 包含:
"notices": [
"该模板用于 AI coding 起步,不是官方系统合约。",
"部署前必须按业务需求、权限模型、Gas 与安全审计进行重写。"
]
AI Agent 在交付生成的合约时,必须同时传递这些 notice。
5.3 消息设计模式
从 msg.rs 模板出发,修改为业务消息:
// 模板中的消息
pub struct InstantiateMsg {
pub owner: Option<String>,
pub count: i32,
}
// 改为业务消息示例——代币质押合约
pub struct InstantiateMsg {
pub admin: Option<String>,
pub unbonding_period_seconds: u64,
pub min_stake: Uint128,
}
设计原则:
InstantiateMsg包含初始化所需的所有参数ExecuteMsg枚举所有写操作,每个变体职责单一QueryMsg枚举所有读操作,每个变体标注#[returns(...)]- 使用
cosmwasm_std::Uint128而非原生整数表示金额 - 权限字段使用
String类型存储地址
5.4 状态设计模式
// 模板中的状态
pub struct State {
pub count: i32,
pub owner: String,
}
// 改为业务状态——使用多个 Item + Map
use cw_storage_plus::{Item, Map};
pub struct Config {
pub admin: String,
pub unbonding_period_seconds: u64,
pub min_stake: Uint128,
}
pub struct StakeInfo {
pub amount: Uint128,
pub start_height: u64,
}
pub const CONFIG: Item<Config> = Item::new("config");
pub const TOTAL_STAKED: Item<Uint128> = Item::new("total_staked");
pub const STAKES: Map<&Addr, StakeInfo> = Map::new("stakes");
5.5 权限模型设计
模板中的权限模式(owner check):
if info.sender.to_string() != state.owner {
return Err(StdError::generic_err("only owner can increment"));
}
对于更复杂的业务,可能需要:
// 使用枚举定义角色
pub enum Role { Admin, Operator, User }
// 使用 Map 存储角色分配
pub const ROLES: Map<&Addr, Role> = Map::new("roles");
fn require_role(deps: &Deps, sender: &Addr, role: Role) -> Result<(), ContractError> {
let actual = ROLES.load(deps.storage, sender)
.map_err(|_| ContractError::Unauthorized {})?;
if actual != role {
return Err(ContractError::Unauthorized {});
}
Ok(())
}
5.6 测试生成策略
AI Agent 需要为每个合约生成以下测试:
#[cfg(test)]
mod tests {
use super::*;
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
use cosmwasm_std::{from_json, Addr};
// 1. Happy path: Instantiate + Query
#[test]
fn proper_instantiation() {
let mut deps = mock_dependencies();
let env = mock_env();
let info = mock_info("creator", &[]);
let msg = InstantiateMsg {
admin: Some("creator".to_string()),
count: 100,
};
let res = instantiate(deps.as_mut(), env, info, msg).unwrap();
assert_eq!(res.attributes.len(), 2);
let query_res = query(deps.as_ref(), mock_env(), QueryMsg::GetCount {}).unwrap();
let value: CountResponse = from_json(&query_res).unwrap();
assert_eq!(value.count, 100);
}
// 2. Permission check: non-owner execute
#[test]
fn non_owner_cannot_increment() {
let mut deps = mock_dependencies();
let env = mock_env();
let info = mock_info("creator", &[]);
let msg = InstantiateMsg {
admin: Some("creator".to_string()),
count: 0,
};
instantiate(deps.as_mut(), env.clone(), info, msg).unwrap();
let attacker = mock_info("attacker", &[]);
let err = execute(deps.as_mut(), env, attacker, ExecuteMsg::Increment {}).unwrap_err();
assert_eq!(err.to_string(), "only owner can increment");
}
// 3. Edge case: reset count
#[test]
fn owner_can_reset() {
let mut deps = mock_dependencies();
let env = mock_env();
let info = mock_info("owner", &[]);
let msg = InstantiateMsg {
admin: Some("owner".to_string()),
count: 42,
};
instantiate(deps.as_mut(), env.clone(), info.clone(), msg).unwrap();
execute(deps.as_mut(), env.clone(), info, ExecuteMsg::Reset { count: 0 }).unwrap();
let query_res = query(deps.as_ref(), env, QueryMsg::GetCount {}).unwrap();
let value: CountResponse = from_json(&query_res).unwrap();
assert_eq!(value.count, 0);
}
}
5.7 错误处理模式
使用 thiserror 定义合约错误:
use cosmwasm_std::StdError;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum ContractError {
#[error("{0}")]
Std(#[from] StdError),
#[error("Unauthorized")]
Unauthorized {},
#[error("Invalid input: {reason}")]
InvalidInput { reason: String },
#[error("Contract is paused")]
ContractPaused {},
}
在 execute 函数中使用:
pub fn execute(deps: DepsMut, _env: Env, info: MessageInfo, msg: ExecuteMsg) -> Result<Response, ContractError> {
match msg {
ExecuteMsg::Increment {} => execute_increment(deps, info),
ExecuteMsg::Reset { count } => execute_reset(deps, info, count),
}
}
fn execute_increment(deps: DepsMut, info: MessageInfo) -> Result<Response, ContractError> {
STATE.update(deps.storage, |mut state| -> Result<_, ContractError> {
if info.sender != state.owner {
return Err(ContractError::Unauthorized {});
}
state.count += 1;
Ok(state)
})?;
Ok(Response::new().add_attribute("action", "increment"))
}
5.8 使用 reference_sources 作为设计参考
模板索引中的 reference_sources 链接到真实合约的消息定义文件:
REF_SOURCES = [
'https://msgchain.org/whitepaper/contracts/cosmwasm/all/genesis_registry_v1/src/msg.rs',
'https://msgchain.org/whitepaper/contracts/cosmwasm/all/agent_registry_v1/src/msg.rs',
]
async def fetch_reference_messages():
for url in REF_SOURCES:
content = await requests.get(url).text()
# 分析消息设计模式,用于指导自己的契约设计
5.9 生成架构检查清单
AI Agent 在交付前应检查:
[ ] 模板的 notices 已传递给开发者
[ ] 反例测试已被替换
[ ] 消息结构符合业务需求
[ ] 状态模型正确
[ ] 权限检查覆盖所有写操作
[ ] Happy path 测试通过
[ ] 权限拒绝测试通过
[ ] 边界/错误输入测试通过
[ ] 代码编译通过: cargo wasm
[ ] 单元测试通过: cargo test
[ ] 配方要求的 7 步流程已完成
[ ] hard_boundaries 已告知开发者
5.10 AI Agent 与配方交互模式
开发者输入业务需求
↓
AI Agent → Step 1: 读取能力矩阵(确认自己能做什么)
↓
AI Agent → Step 2: 读取 contract/registry/rpc 模块(建立链路理解)
↓
AI Agent → Step 3: 获取 counter_v1 模板 + 修改为业务合约
↓
AI Agent → Step 4: 生成 cargo test + 运行确认
↓
AI Agent → Step 5: 输出部署计划(StoreCode → Instantiate → Query → Execute)
↓
AI Agent → Step 6: 建议证据收集流程
↓
AI Agent → Step 7: 标记人类上线门禁点
↓
开发者确认并执行上线
6. 边界声明
6.1 starter_ready ≠ production
contract_template_pack.machine_readiness = "starter_ready"
contract_template_pack.production_supported = false
starter_ready 表示模板可以用于快速起步和原型验证,但不等于
可以直接用于生产环境。
| starter_ready 的含义 | production_supported false 的含义 |
|---|---|
| 可快速生成最小可运行合约 | 没有经过开发参考级审计 |
| 可用作文档示例和教学 | 不覆盖所有业务模式 |
| 适合集成到 AI Agent 工作流 | 没有替代业务审计与测试 |
| 可作为测试骨架起点 | 部署前必须按业务需求重写 |
6.2 模板包的阻塞性缺口
来自 developer_capability_matrix.json 的 blocking_gaps:
1. 模板仍是 starter,不覆盖所有业务模式
2. 没有替代业务审计与测试
这意味着:
- 模板只覆盖计数器模式,不覆盖质押、拍卖、借贷等复杂业务
- 模板生成的代码没有经过专业审计
- AI Agent 必须为业务逻辑添加完整的测试覆盖
- 高价值场景需要独立的安全审计
6.3 模板包的边界
来自 developer_capability_matrix.json 的 boundaries:
模板包用于加速 AI coding,不等于官方系统合约或经过审计的业务模板。
6.4 运行时的边界
来自 contract_runtime 的 blocking_gaps:
1. 当前模板是 starter pack,不等于官方业务合约全集
2. 缺少从链实现自动导出的正式 schema 契约
3. 生产部署仍需真实权限与验收
来自 contract_runtime 的 boundaries:
运行时主线已实现,且已补 starter template,但不等于所有业务模板都已完善。
AI 可辅助生成合约代码,但仍需结合业务规则、人类审核与真实部署账户完成落地。
6.5 配方硬边界
来自 contract_minimal.json 的 hard_boundaries:
1. 当前 starter template pack 只提供起步骨架,不等于业务合约已自动完成。
2. 没有真实钱包、私钥、部署权限与 Gas 预算,AI 不能独立完成最终上链。
3. 高价值或治理相关动作必须经过人类审批与门禁验证。
6.6 认证与部署门禁
AI Agent 生成合约代码后的上线路径必须包含:
┌─────────────────────────────────────────────────────┐
│ AI Agent 输出 │
│ 合约源码 · cargo test · 部署计划 · 边界说明 │
└──────────────────┬──────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ 开发者/运维工程师 检查 │
│ - 代码审查 │
│ - 业务逻辑验证 │
│ - 权限模型确认 │
│ - Gas 估算 │
└──────────────────┬──────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ 签名与部署 │
│ - 使用私钥签名交易 │
│ - Gas 预算配置 │
│ - StoreCode 广播 │
│ - Instantiate 广播 │
└──────────────────┬──────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ 回执验证 │
│ - tx hash 确认 │
│ - query replay │
│ - receipt 存储 │
└─────────────────────────────────────────────────────┘
6.7 不能做的事
AI Agent 在使用模板和配方时,不能:
| 不能做的事 | 原因 |
|---|---|
| 说模板是开发参考级 | production_supported = false |
| 说 AI 可以独立上链 | 没有钱包/私钥/Gas |
| 省略 hard_boundaries 通知 | 配方明确要求传递 |
| 保留反例测试 | 测试必须替换 |
| 使用未授权的 CosmosMsg | BankMsg/Gov/Staking/IBC 仍 fail-closed |
| 绕过人类审批 | 治理相关动作必须审批 |
6.8 能力矩阵总览
以下列出 MSG Chain 所有开发表面的 machine_readiness(部分与合约开发间接相关):
| surface_id | machine_readiness | production_supported | AI Agent 建议 |
|---|---|---|---|
| contract_template_pack | starter_ready | false | 可用作代码生成起点 |
| contract_runtime | assisted_codegen | true | 可辅助设计但需人类确认 |
| core_contract_reference_pack | source_backed_reference | false | 只读参考 |
| registry_resolution | production_reference | true | 可在代码中引用 |
| rpc_gateway | assisted_codegen | true | 可生成查询/广播代码 |
| formal_api_schema_pack | source_backed_reference | false | 参考用,不可写 |
| wallet_frontend | guarded_integration | false | 谨慎集成 |
| explorer_receipts | read_only_assist | false | 只读辅助 |
| agent_query_and_guarded_write | guarded_write | false | 受保护,AI 不能写 |
| sdk_surface | local_candidate | false | 本地候选 |
| chain_config_pack | starter_ready | true | 可生成链配置 |
| public_sandbox_strategy | fail_closed_reference | false | 默认不可用 |
| dapp_starter_pack | starter_ready | false | 可作 dApp 起步 |
6.9 边界自查清单
AI Agent 在交付每个合约生成任务时,应逐项核对:
[ ] 我是否告知开发者:模板是 starter,不是开发参考级?
[ ] 我是否告知开发者:AI 不能独立完成最终上链?
[ ] 我是否告知开发者:高价值动作需人工审批?
[ ] 我是否已获取:业务目标与状态模型?
[ ] 我是否已获取:管理员/多签/治理控制要求?
[ ] 我是否已获取:部署网络与目标地址策略?
[ ] 我是否已获取:真实签名账户、Gas 预算与发布窗口?
[ ] 我是否有完整的 cargo test 覆盖?
[ ] 部署计划是否包含 StoreCode / Instantiate / Query / Execute / Receipt?
[ ] 是否已标记人类上线门禁点?
7. 完整示例
7.1 场景:构建一个简单的任务管理合约
本节演示从模板出发,经过配方工作流,最终形成完整合约代码的过程。
7.2 Step 1: 读取能力矩阵
import requests
MATRIX_URL = 'https://msgchain.org/whitepaper/developer_capability_matrix.json'
matrix = requests.get(MATRIX_URL).json()
for item in matrix['items']:
sid = item['surface_id']
ready = item['machine_readiness']
prod = item['production_supported']
print(f'{sid}: {ready} (prod={prod})')
AI Agent 读取后确认:
contract_template_pack是starter_ready,可以用来起步contract_runtime是assisted_codegen,可以辅助设计- 两者组合可以完成代码生成 + 部署规划
7.3 Step 2: 读取模块
CONTRACT_EXPORT = 'https://msgchain.org/whitepaper/module_exports/contract.json'
REGISTRY_EXPORT = 'https://msgchain.org/whitepaper/module_exports/registry.json'
RPC_EXPORT = 'https://msgchain.org/whitepaper/module_exports/rpc.json'
contract_mod = requests.get(CONTRACT_EXPORT).json()
registry_mod = requests.get(REGISTRY_EXPORT).json()
rpc_mod = requests.get(RPC_EXPORT).json()
print(f'Contract status: {contract_mod["status_label"]}')
print(f'Registry status: {registry_mod["status_label"]}')
print(f'RPC status: {rpc_mod["status_label"]}')
AI Agent 确认三个模块都已实现。
7.4 Step 3: 从模板生成合约
从 counter_v1 模板获取文件,然后修改为任务管理合约。
Cargo.toml(保持模板基本不变)
[package]
name = "msg-task-manager-v1"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
cosmwasm-schema = "1.5.0"
cosmwasm-std = "1.5.0"
cw-storage-plus = "1.2.0"
serde = { version = "1.0", default-features = false, features = ["derive"] }
thiserror = "1.0"
src/msg.rs — 业务消息定义
use cosmwasm_schema::{cw_serde, QueryResponses};
use cosmwasm_std::Uint128;
#[cw_serde]
pub struct InstantiateMsg {
pub owner: Option<String>,
pub task_limit: u32,
}
#[cw_serde]
pub enum ExecuteMsg {
CreateTask {
title: String,
description: String,
reward: Uint128,
},
CompleteTask {
task_id: u64,
},
CancelTask {
task_id: u64,
},
UpdateConfig {
task_limit: u32,
},
}
#[cw_serde]
#[derive(QueryResponses)]
pub enum QueryMsg {
#[returns(TaskResponse)]
GetTask { task_id: u64 },
#[returns(TasksResponse)]
ListTasks {},
#[returns(ConfigResponse)]
GetConfig {},
}
#[cw_serde]
pub struct TaskResponse {
pub task_id: u64,
pub creator: String,
pub title: String,
pub description: String,
pub reward: Uint128,
pub completed: bool,
pub cancelled: bool,
}
#[cw_serde]
pub struct TasksResponse {
pub tasks: Vec<TaskResponse>,
}
#[cw_serde]
pub struct ConfigResponse {
pub owner: String,
pub task_limit: u32,
pub task_count: u64,
}
src/state.rs — 状态定义
use cosmwasm_schema::cw_serde;
use cosmwasm_std::Uint128;
use cw_storage_plus::{Item, Map};
#[cw_serde]
pub struct Config {
pub owner: String,
pub task_limit: u32,
pub task_count: u64,
}
#[cw_serde]
pub struct Task {
pub task_id: u64,
pub creator: String,
pub title: String,
pub description: String,
pub reward: Uint128,
pub completed: bool,
pub cancelled: bool,
}
pub const CONFIG: Item<Config> = Item::new("config");
pub const TASKS: Map<u64, Task> = Map::new("tasks");
src/contract.rs — 合约入口
use cosmwasm_std::{
entry_point, to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response,
StdError,
};
use thiserror::Error;
use crate::msg::{
ConfigResponse, ExecuteMsg, InstantiateMsg, QueryMsg, TaskResponse, TasksResponse,
};
use crate::state::{Config, Task, CONFIG, TASKS};
#[derive(Error, Debug)]
pub enum ContractError {
#[error("{0}")]
Std(#[from] StdError),
#[error("Unauthorized")]
Unauthorized {},
#[error("Task limit reached: {limit}")]
TaskLimitReached { limit: u32 },
#[error("Task not found: {task_id}")]
TaskNotFound { task_id: u64 },
#[error("Task already completed")]
AlreadyCompleted {},
#[error("Task already cancelled")]
AlreadyCancelled {},
}
#[entry_point]
pub fn instantiate(
deps: DepsMut,
_env: Env,
info: MessageInfo,
msg: InstantiateMsg,
) -> Result<Response, ContractError> {
let owner = msg.owner.unwrap_or_else(|| info.sender.to_string());
let config = Config {
owner,
task_limit: msg.task_limit,
task_count: 0,
};
CONFIG.save(deps.storage, &config)?;
Ok(Response::new()
.add_attribute("action", "instantiate")
.add_attribute("owner", &config.owner))
}
#[entry_point]
pub fn execute(
deps: DepsMut,
_env: Env,
info: MessageInfo,
msg: ExecuteMsg,
) -> Result<Response, ContractError> {
match msg {
ExecuteMsg::CreateTask { title, description, reward } => {
execute_create_task(deps, info, title, description, reward)
}
ExecuteMsg::CompleteTask { task_id } => execute_complete_task(deps, info, task_id),
ExecuteMsg::CancelTask { task_id } => execute_cancel_task(deps, info, task_id),
ExecuteMsg::UpdateConfig { task_limit } => execute_update_config(deps, info, task_limit),
}
}
fn execute_create_task(
deps: DepsMut,
info: MessageInfo,
title: String,
description: String,
reward: Uint128,
) -> Result<Response, ContractError> {
let mut config = CONFIG.load(deps.storage)?;
if config.task_count >= config.task_limit {
return Err(ContractError::TaskLimitReached {
limit: config.task_limit,
});
}
let task_id = config.task_count + 1;
let task = Task {
task_id,
creator: info.sender.to_string(),
title,
description,
reward,
completed: false,
cancelled: false,
};
TASKS.save(deps.storage, task_id, &task)?;
config.task_count = task_id;
CONFIG.save(deps.storage, &config)?;
Ok(Response::new()
.add_attribute("action", "create_task")
.add_attribute("task_id", task_id.to_string())
.add_attribute("creator", &task.creator))
}
fn execute_complete_task(
deps: DepsMut,
info: MessageInfo,
task_id: u64,
) -> Result<Response, ContractError> {
let config = CONFIG.load(deps.storage)?;
if info.sender.to_string() != config.owner {
return Err(ContractError::Unauthorized {});
}
let mut task = TASKS.load(deps.storage, task_id)
.map_err(|_| ContractError::TaskNotFound { task_id })?;
if task.completed {
return Err(ContractError::AlreadyCompleted {});
}
if task.cancelled {
return Err(ContractError::AlreadyCancelled {});
}
task.completed = true;
TASKS.save(deps.storage, task_id, &task)?;
Ok(Response::new()
.add_attribute("action", "complete_task")
.add_attribute("task_id", task_id.to_string()))
}
fn execute_cancel_task(
deps: DepsMut,
info: MessageInfo,
task_id: u64,
) -> Result<Response, ContractError> {
let mut task = TASKS.load(deps.storage, task_id)
.map_err(|_| ContractError::TaskNotFound { task_id })?;
if info.sender.to_string() != task.creator {
return Err(ContractError::Unauthorized {});
}
if task.completed {
return Err(ContractError::AlreadyCompleted {});
}
if task.cancelled {
return Err(ContractError::AlreadyCancelled {});
}
task.cancelled = true;
TASKS.save(deps.storage, task_id, &task)?;
Ok(Response::new()
.add_attribute("action", "cancel_task")
.add_attribute("task_id", task_id.to_string()))
}
fn execute_update_config(
deps: DepsMut,
info: MessageInfo,
task_limit: u32,
) -> Result<Response, ContractError> {
let mut config = CONFIG.load(deps.storage)?;
if info.sender.to_string() != config.owner {
return Err(ContractError::Unauthorized {});
}
config.task_limit = task_limit;
CONFIG.save(deps.storage, &config)?;
Ok(Response::new()
.add_attribute("action", "update_config")
.add_attribute("task_limit", task_limit.to_string()))
}
#[entry_point]
pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> Result<Binary, ContractError> {
match msg {
QueryMsg::GetTask { task_id } => query_task(deps, task_id),
QueryMsg::ListTasks {} => query_list_tasks(deps),
QueryMsg::GetConfig {} => query_config(deps),
}
}
fn query_task(deps: Deps, task_id: u64) -> Result<Binary, ContractError> {
let task = TASKS.load(deps.storage, task_id)
.map_err(|_| ContractError::TaskNotFound { task_id })?;
Ok(to_json_binary(&TaskResponse {
task_id: task.task_id,
creator: task.creator,
title: task.title,
description: task.description,
reward: task.reward,
completed: task.completed,
cancelled: task.cancelled,
})?)
}
fn query_list_tasks(deps: Deps) -> Result<Binary, ContractError> {
let tasks: Vec<TaskResponse> = TASKS
.range(deps.storage, None, None, cosmwasm_std::Order::Ascending)
.map(|item| {
let (_, task) = item.unwrap();
TaskResponse {
task_id: task.task_id,
creator: task.creator,
title: task.title,
description: task.description,
reward: task.reward,
completed: task.completed,
cancelled: task.cancelled,
}
})
.collect();
Ok(to_json_binary(&TasksResponse { tasks })?)
}
fn query_config(deps: Deps) -> Result<Binary, ContractError> {
let config = CONFIG.load(deps.storage)?;
Ok(to_json_binary(&ConfigResponse {
owner: config.owner,
task_limit: config.task_limit,
task_count: config.task_count,
})?)
}
7.5 Step 4: 生成测试
#[cfg(test)]
mod tests {
use super::*;
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
use cosmwasm_std::{from_json, Uint128};
fn setup_contract() -> (cosmwasm_std::OwnedDeps<cosmwasm_std::MemoryStorage, cosmwasm_std::testing::MockApi, cosmwasm_std::testing::MockQuerier>, cosmwasm_std::Env) {
let mut deps = mock_dependencies();
let env = mock_env();
let info = mock_info("admin", &[]);
let msg = InstantiateMsg {
owner: Some("admin".to_string()),
task_limit: 10,
};
instantiate(deps.as_mut(), env.clone(), info, msg).unwrap();
(deps, env)
}
#[test]
fn proper_instantiation() {
let (deps, _) = setup_contract();
let config: ConfigResponse = from_json(
&query(deps.as_ref(), mock_env(), QueryMsg::GetConfig {}).unwrap()
).unwrap();
assert_eq!(config.owner, "admin");
assert_eq!(config.task_limit, 10);
assert_eq!(config.task_count, 0);
}
#[test]
fn create_and_query_task() {
let (mut deps, env) = setup_contract();
let creator = mock_info("creator", &[]);
execute(
deps.as_mut(),
env.clone(),
creator,
ExecuteMsg::CreateTask {
title: "Build AI Agent".to_string(),
description: "Design agent for contract codegen".to_string(),
reward: Uint128::new(1000),
},
).unwrap();
let task: TaskResponse = from_json(
&query(deps.as_ref(), env.clone(), QueryMsg::GetTask { task_id: 1 }).unwrap()
).unwrap();
assert_eq!(task.title, "Build AI Agent");
assert_eq!(task.creator, "creator");
assert_eq!(task.reward, Uint128::new(1000));
assert!(!task.completed);
assert!(!task.cancelled);
}
#[test]
fn non_admin_cannot_complete_task() {
let (mut deps, env) = setup_contract();
let creator = mock_info("creator", &[]);
execute(
deps.as_mut(),
env.clone(),
creator.clone(),
ExecuteMsg::CreateTask {
title: "Task 1".to_string(),
description: "Test task".to_string(),
reward: Uint128::new(500),
},
).unwrap();
let attacker = mock_info("attacker", &[]);
let err = execute(
deps.as_mut(),
env.clone(),
attacker,
ExecuteMsg::CompleteTask { task_id: 1 },
).unwrap_err();
assert!(matches!(err, ContractError::Unauthorized {}));
}
#[test]
fn creator_can_cancel_own_task() {
let (mut deps, env) = setup_contract();
let creator = mock_info("creator", &[]);
execute(
deps.as_mut(),
env.clone(),
creator.clone(),
ExecuteMsg::CreateTask {
title: "Cancellable".to_string(),
description: "Will be cancelled".to_string(),
reward: Uint128::new(100),
},
).unwrap();
execute(
deps.as_mut(),
env.clone(),
creator,
ExecuteMsg::CancelTask { task_id: 1 },
).unwrap();
let task: TaskResponse = from_json(
&query(deps.as_ref(), env, QueryMsg::GetTask { task_id: 1 }).unwrap()
).unwrap();
assert!(task.cancelled);
}
#[test]
fn task_limit_enforced() {
let (mut deps, env) = setup_contract();
let creator = mock_info("creator", &[]);
for i in 0..10 {
execute(
deps.as_mut(),
env.clone(),
creator.clone(),
ExecuteMsg::CreateTask {
title: format!("Task {}", i),
description: "test".to_string(),
reward: Uint128::new(1),
},
).unwrap();
}
let err = execute(
deps.as_mut(),
env.clone(),
creator,
ExecuteMsg::CreateTask {
title: "Overflow".to_string(),
description: "Should fail".to_string(),
reward: Uint128::new(1),
},
).unwrap_err();
assert!(matches!(err, ContractError::TaskLimitReached { .. }));
}
#[test]
fn list_tasks() {
let (mut deps, env) = setup_contract();
let creator = mock_info("creator", &[]);
execute(
deps.as_mut(),
env.clone(),
creator.clone(),
ExecuteMsg::CreateTask {
title: "Task A".to_string(),
description: "First".to_string(),
reward: Uint128::new(100),
},
).unwrap();
execute(
deps.as_mut(),
env.clone(),
creator,
ExecuteMsg::CreateTask {
title: "Task B".to_string(),
description: "Second".to_string(),
reward: Uint128::new(200),
},
).unwrap();
let list: TasksResponse = from_json(
&query(deps.as_ref(), env, QueryMsg::ListTasks {}).unwrap()
).unwrap();
assert_eq!(list.tasks.len(), 2);
assert_eq!(list.tasks[0].title, "Task A");
assert_eq!(list.tasks[1].title, "Task B");
}
}
7.6 Step 5: 部署计划
"""
部署计划: msg-task-manager-v1
链 ID: msg-chain-1
网络: msg-chain-1
=== 步骤 1: 编译 ===
cargo wasm
# 输出: target/wasm32-unknown-unknown/release/msg_task_manager_v1.wasm
=== 步骤 2: 优化 ===
cargo run-script optimize
# 输出: artifacts/msg_task_manager_v1.wasm
=== 步骤 3: StoreCode ===
# 使用 CosmJS 或直接 RPC 广播
wasm_bytecode = open('artifacts/msg_task_manager_v1.wasm', 'rb').read()
tx = {
'jsonrpc': '2.0',
'method': 'broadcast_tx_commit',
'params': {
'tx': {
'type': 'wasm/StoreCode',
'value': {
'sender': '<DEPLOYER_ADDRESS>',
'wasm_byte_code': wasm_bytecode.hex(),
}
}
},
'id': 1
}
# 响应中的 code_id 用于下一步
=== 步骤 4: Instantiate ===
code_id = 1 # 从上一步获取
instantiate_msg = {
'owner': None, # 默认为部署者
'task_limit': 100,
}
tx = {
'jsonrpc': '2.0',
'method': 'broadcast_tx_commit',
'params': {
'tx': {
'type': 'wasm/Instantiate',
'value': {
'sender': '<DEPLOYER_ADDRESS>',
'code_id': str(code_id),
'msg': instantiate_msg,
'label': 'msg-task-manager-v1',
'admin': '<DEPLOYER_ADDRESS>',
'funds': []
}
}
},
'id': 1
}
# 响应中的 contract_address 用于后续调用
=== 步骤 5: Query 验证 ===
contract_addr = '<CONTRACT_ADDRESS>'
query = {
'jsonrpc': '2.0',
'method': 'abci_query',
'params': {
'path': f'/wasm/contract/{contract_addr}',
'data': {'get_config': {}},
'height': '0',
'prove': False
},
'id': 1
}
=== 步骤 6: Execute 测试 ===
execute_msg = {
'create_task': {
'title': 'Test Task',
'description': 'Verify deployment',
'reward': '1000'
}
}
tx = {
'jsonrpc': '2.0',
'method': 'broadcast_tx_commit',
'params': {
'tx': {
'type': 'wasm/Execute',
'value': {
'sender': '<USER_ADDRESS>',
'contract': contract_addr,
'msg': execute_msg,
'funds': []
}
}
},
'id': 1
}
=== 步骤 7: Receipt 校验 ===
tx_hash = '<TX_HASH>'
receipt_query = {
'jsonrpc': '2.0',
'method': 'tx',
'params': [tx_hash, True],
'id': 1
}
"""
7.7 Step 6: 证据收集建议
"""
证据收集清单:
1. tx_hash:
- StoreCode: <从 broadcast_tx_commit 响应获取>
- Instantiate: <从 broadcast_tx_commit 响应获取>
- Execute: <从 broadcast_tx_commit 响应获取>
2. receipt:
- 每个交易对应的 receipt JSON
3. query replay:
- abci_query 返回的合约状态 JSON
4. evidence refs:
- contract.html: https://msgchain.org/whitepaper/module_exports/contract.json
- registry.html: https://msgchain.org/whitepaper/module_exports/registry.json
- rpc.html: https://msgchain.org/whitepaper/module_exports/rpc.json
- recipe: https://msgchain.org/whitepaper/recipes/contract_minimal.json
- template: https://msgchain.org/whitepaper/contract_templates/counter_v1/manifest.json
"""
7.8 Step 7: 人类上线门禁通知
HUMAN_GATE_NOTICE = """
=== 人类上线门禁检查清单 ===
请确认以下事项后再执行部署:
[ ] 1. 密钥安全:
- 部署账户的私钥已安全存储
- 测试环境与生产环境使用不同的密钥
- 多签账户的阈值已确认
[ ] 2. 治理条件:
- 如果是治理相关合约,提案已通过
- 如果是系统合约,已获得相应授权
[ ] 3. 上线窗口:
- 当前网络无异常
- Gas 价格稳定
- 无正在进行的紧急维护
[ ] 4. 生产风险:
- 合约代码已通过同行审查
- 测试覆盖了 happy path 和错误路径
- Gas 消耗在可接受范围内
- 合约管理员在必要时可以暂停或升级
[ ] 5. 回滚计划:
- 如果部署失败,已知回滚步骤
- 旧版本的 code_id 仍可访问
- 必要时可以重新 Instantiate 旧版本
=== 模板通知(来自 counter_v1 manifest) ===
- 该模板用于 AI coding 起步,不是官方系统合约。
- 部署前必须按业务需求、权限模型、Gas 与安全审计进行重写。
=== 配方硬边界(来自 contract_minimal.json) ===
- 当前 starter template pack 只提供起步骨架,不等于业务合约已自动完成。
- 没有真实钱包、私钥、部署权限与 Gas 预算,AI 不能独立完成最终上链。
- 高价值或治理相关动作必须经过人类审批与门禁验证。
"""
7.9 完整的 AI Agent 工作流脚本
#!/usr/bin/env python3
"""
MSG Chain 合约 AI Coding 最小闭环
基于 recipes/contract_minimal.json 的 7 步工作流
"""
import requests
import json
import os
import sys
from typing import Optional
BASE = 'https://msgchain.org/whitepaper'
MATRIX_URL = f'{BASE}/developer_capability_matrix.json'
RECIPE_URL = f'{BASE}/recipes/contract_minimal.json'
TEMPLATE_INDEX_URL = f'{BASE}/contract_templates/index.json'
COUNTER_MANIFEST_URL = f'{BASE}/contract_templates/counter_v1/manifest.json'
async def step1_read_capability_matrix():
matrix = await requests.get(MATRIX_URL).json()
relevant = [i for i in matrix['items']
if i['surface_id'] in ('contract_template_pack', 'contract_runtime')]
for item in relevant:
print(f"[{item['surface_id']}] readiness={item['machine_readiness']}, "
f"prod={item['production_supported']}")
return relevant
async def step2_read_modules():
modules = [
f'{BASE}/module_exports/contract.json',
f'{BASE}/module_exports/registry.json',
f'{BASE}/module_exports/rpc.json',
]
for url in modules:
mod = await requests.get(url).json()
print(f"[module] {mod['title']}: {mod['status_label']}")
return True
async def step3_generate_contract(name: str, target_dir: str):
manifest = await requests.get(COUNTER_MANIFEST_URL).json()
for filepath in manifest['entry_files']:
url = f'{BASE}/{filepath}'
content = await requests.get(url).text
relpath = filepath.replace('contract_templates/counter_v1/', '')
fullpath = os.path.join(target_dir, relpath)
os.makedirs(os.path.dirname(fullpath), exist_ok=True)
with open(fullpath, 'w') as f:
f.write(content)
print(f"[generate] {len(manifest['entry_files'])} files written to {target_dir}")
return True
async def run_workflow(name: str, target_dir: str):
print("=== Step 1: 读取能力矩阵 ===")
await step1_read_capability_matrix()
print("\n=== Step 2: 读取模块 ===")
await step2_read_modules()
print(f"\n=== Step 3: 生成合约骨架 ===")
await step3_generate_contract(name, target_dir)
print(f"\n=== Step 4: 编写测试(手动) ===")
print(f"编辑 {target_dir}/tests/integration.rs 添加业务测试")
print(f"\n=== Step 5: 部署计划 ===")
print(f"人工编写 {target_dir}/DEPLOY_PLAN.md")
print(f"\n=== Step 6: 证据收集 ===")
print(f"部署后收集 tx hash / receipt / query replay")
print(f"\n=== Step 7: 人类上线门禁 ===")
print(f"参考 recipe 中的 hard_boundaries 通知开发者")
if __name__ == '__main__':
import asyncio
asyncio.run(run_workflow('msg-task-manager-v1', './output/msg-task-manager-v1'))
7.10 完整产出物清单
output/msg-task-manager-v1/
├── Cargo.toml # 编译配置
├── src/
│ ├── msg.rs # 消息定义
│ ├── state.rs # 状态模型
│ └── contract.rs # 合约入口 + 业务逻辑
├── tests/
│ └── integration.rs # 完整测试覆盖
├── DEPLOY_PLAN.md # 部署计划
├── EVIDENCE.md # 证据收集指导
└── HUMAN_GATE.md # 人类上线门禁清单
7.11 边界提醒
FINAL_NOTICE = """
本合约代码基于 MSG Chain `counter_v1` starter 模板生成,
并按照 `contract_minimal.json` 配方的 7 步工作流交付。
⚠️ 边界提醒:
1. 模板是 starter,不是开发参考级代码。
2. AI 不能独立完成最终上链。
3. 高价值或治理相关动作必须经过人类审批。
请完成以下操作后再上线:
1. 同行代码审查
2. 业务逻辑验证
3. 权限模型确认
4. Gas 消耗评估
5. 安全审计(高价值场景)
"""
附录 A: 数据来源摘要
A.1 contract_templates/index.json
{
"schema_version": "v1",
"templates": [
{
"id": "counter_v1",
"public_url": "https://msgchain.org/whitepaper/contract_templates/counter_v1/manifest.json",
"description": "Minimal CosmWasm starter template for AI-assisted contract coding on MSG."
}
],
"reference_sources": [
"contracts/cosmwasm/all/genesis_registry_v1/src/msg.rs",
"contracts/cosmwasm/all/agent_registry_v1/src/msg.rs"
],
"metadata_profile": "public_stable"
}
A.2 contract_templates/counter_v1/manifest.json
{
"template_id": "counter_v1",
"template_kind": "starter",
"entry_files": [
"contract_templates/counter_v1/Cargo.toml",
"contract_templates/counter_v1/src/msg.rs",
"contract_templates/counter_v1/src/state.rs",
"contract_templates/counter_v1/src/contract.rs",
"contract_templates/counter_v1/tests/integration.rs"
],
"notices": [
"该模板用于 AI coding 起步,不是官方系统合约。",
"部署前必须按业务需求、权限模型、Gas 与安全审计进行重写。"
],
"metadata_profile": "public_stable"
}
A.3 recipes/contract_minimal.json
{
"recipe_id": "contract_minimal",
"title": "MSG 合约 AI Coding 最小闭环",
"workflow": [
{"step": 1, "action": "read developer_entry + capability matrix"},
{"step": 2, "action": "read contract/rpc/registry modules and chunks"},
{"step": 3, "action": "generate contract message and state design"},
{"step": 4, "action": "generate cargo tests and failure cases"},
{"step": 5, "action": "prepare deploy plan"},
{"step": 6, "action": "collect tx hash, receipt, query replay, evidence refs"},
{"step": 7, "action": "human release gate"}
],
"hard_boundaries": [
"当前 starter template pack 只提供起步骨架,不等于业务合约已自动完成。",
"没有真实钱包、私钥、部署权限与 Gas 预算,AI 不能独立完成最终上链。",
"高价值或治理相关动作必须经过人类审批与门禁验证。"
],
"metadata_profile": "public_stable"
}
A.4 developer_capability_matrix.json (相关条目)
{
"surface_id": "contract_template_pack",
"machine_readiness": "starter_ready",
"production_supported": false,
"best_for": ["快速生成最小可运行合约", "补充消息 schema", "生成 cargo test 骨架"],
"blocking_gaps": ["模板仍是 starter,不覆盖所有业务模式", "没有替代业务审计与测试"],
"boundaries": ["模板包用于加速 AI coding,不等于官方系统合约或经过审计的业务模板。"]
}
{
"surface_id": "contract_runtime",
"machine_readiness": "assisted_codegen",
"production_supported": true,
"best_for": ["合约消息设计", "状态流转设计", "部署调用路径生成", "回执验证规划"],
"blocking_gaps": [
"当前模板是 starter pack,不等于官方业务合约全集",
"缺少从链实现自动导出的正式 schema 契约",
"生产部署仍需真实权限与验收"
]
}
附录 B: 常用 API 端点速查
| 用途 | 端点 | 方法 |
|---|---|---|
| 广播交易 | POST /broadcast_tx_commit |
JSON-RPC |
| ABCI 查询 | GET /abci_query |
JSON-RPC |
| 合约部署 | POST /api/v1/contracts/deploy |
REST |
| 合约实例化 | POST /api/v1/contracts/instantiate |
REST |
| 合约执行 | POST /api/v1/contracts/execute |
REST |
| 账户余额 | GET /api/v1/bank/balances |
REST |
| 回执查询 | GET /api/v1/receipts |
REST |
附录 C: 术语表
| 术语 | 说明 |
|---|---|
| CosmWasm | Cosmos 生态的 WebAssembly 智能合约引擎 |
| canonical key | 注册中心中的规范合约键名 |
| store_code | 上传合约 wasm 字节码到链上 |
| instantiate | 从已存储的代码创建合约实例 |
| execute | 调用合约的写操作 |
| query | 调用的合约读操作 |
| migrate | 更新合约到新的代码版本 |
| submessage | 合约内部发起的子消息调用 |
| reply | submessage 执行后的回调处理 |
| receipt | 交易执行后的回执 |
| fail-closed | 默认拒绝的安全策略 |
| starter_ready | 可以用于起步但不能直接上生产 |
| assisted_codegen | AI 辅助代码生成但需人类确认 |
本文档基于 MSG Chain 白皮书公开数据编写。
所有代码示例仅供学习参考,部署生产前请务必经过审计。
链 ID: msg-chain-1
