name: midnight-tooling:lifecycle-management description: 用于管理已部署合约的生命周期,检查合约状态,在升级前备份状态,规划合约迁移,实施版本控制策略,或优雅地弃用合约。
合约生命周期管理
管理已部署的Midnight合约的整个生命周期,包括状态检查、备份、版本控制、迁移和优雅弃用。
何时使用
- 为调试或监控检查当前合约状态
- 在升级前备份合约状态
- 规划和执行合约迁移
- 实施版本控制策略
- 优雅地弃用旧合约版本
- 管理多版本合约部署
核心概念
合约生命周期阶段
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌──────────┐
│ 部署 │───▶│ 活跃 │───▶│ 迁移 │───▶│ 日落 │───▶│ 归档 │
└─────────┘ └─────────┘ └─────────┘ └─────────┘ └──────────┘
│ │
│ ▼
│ ┌─────────┐
└────────▶│ 监控 │
└─────────┘
不可变性考量
Midnight合约一旦部署即不可变。“升级”需要:
- 部署新合约版本
- 将状态从旧合约迁移到新合约
- 更新引用(例如,在前端、其他合约中)
- 弃用旧版本
状态迁移模式
| 模式 | 描述 | 使用场景 |
|---|---|---|
| 快照与部署 | 导出状态,部署包含状态的新合约 | 简单迁移 |
| 代理模式 | 使用指向实现的代理合约 | 频繁升级 |
| 双写 | 同时写入新旧合约 | 零停机时间迁移 |
| 惰性迁移 | 按需迁移状态 | 大型状态迁移 |
参考文档
| 文档 | 描述 |
|---|---|
| state-inspection.md | 查询和分析合约状态 |
| migration-patterns.md | 合约升级策略 |
示例
| 示例 | 描述 |
|---|---|
| state-backup/ | 导出和备份合约状态 |
| contract-upgrade/ | 迁移到新合约版本 |
快速开始
1. 检查当前状态
import { connectContract } from '@midnight-ntwrk/midnight-js-contracts';
import { createIndexerClient } from '@midnight-ntwrk/midnight-js-indexer';
// 连接到合约
const contract = await connectContract({
address: CONTRACT_ADDRESS,
artifact: Contract,
wallet,
config,
});
// 通过电路查询状态
const state = await contract.query.get_state();
console.log('当前状态:', state);
// 或者通过索引器查询原始状态
const indexer = createIndexerClient({ url: config.indexer });
const rawState = await indexer.getContractState(CONTRACT_ADDRESS);
2. 备份状态
interface StateBackup {
contractAddress: string;
blockHeight: number;
timestamp: string;
state: Record<string, unknown>;
}
async function backupState(
contract: ConnectedContract,
indexer: IndexerClient
): Promise<StateBackup> {
const state = await contract.query.get_full_state();
const info = await indexer.getContractInfo(contract.address);
return {
contractAddress: contract.address,
blockHeight: info.lastUpdateBlock,
timestamp: new Date().toISOString(),
state,
};
}
3. 部署新版本
import { NewContract } from './build-v2/contract.cjs';
const newContract = await deployContract({
wallet,
artifact: NewContract,
initialState: {
...backupData.state,
version: 2,
},
config,
});
console.log('新版本部署于:', newContract.address);
常见模式
版本注册表
跟踪多个合约版本:
interface ContractVersion {
version: number;
address: string;
deployedAt: string;
status: 'active' | 'deprecated' | 'archived';
}
class VersionRegistry {
private versions: ContractVersion[] = [];
addVersion(version: ContractVersion): void {
this.versions.push(version);
}
getActiveVersion(): ContractVersion | undefined {
return this.versions.find((v) => v.status === 'active');
}
deprecateVersion(version: number): void {
const v = this.versions.find((v) => v.version === version);
if (v) {
v.status = 'deprecated';
}
}
getAllVersions(): ContractVersion[] {
return [...this.versions].sort((a, b) => b.version - a.version);
}
}
健康监控
interface ContractHealth {
address: string;
isResponding: boolean;
lastActivity: Date | null;
stateConsistent: boolean;
warnings: string[];
}
async function checkContractHealth(
contract: ConnectedContract,
indexer: IndexerClient
): Promise<ContractHealth> {
const warnings: string[] = [];
let isResponding = false;
let lastActivity: Date | null = null;
let stateConsistent = true;
try {
// 测试响应性
await contract.query.get_version();
isResponding = true;
} catch {
warnings.push('合约未响应查询');
}
try {
// 检查最后活动
const info = await indexer.getContractInfo(contract.address);
lastActivity = new Date(info.lastUpdateTimestamp);
// 如果长时间无活动则警告
const daysSinceActivity = (Date.now() - lastActivity.getTime()) / (1000 * 60 * 60 * 24);
if (daysSinceActivity > 30) {
warnings.push(`${daysSinceActivity.toFixed(0)} 天无活动`);
}
} catch {
warnings.push('无法获取合约信息');
}
return {
address: contract.address,
isResponding,
lastActivity,
stateConsistent,
warnings,
};
}
迁移脚本模板
interface MigrationPlan {
sourceVersion: number;
targetVersion: number;
sourceAddress: string;
steps: MigrationStep[];
}
interface MigrationStep {
name: string;
execute: () => Promise<void>;
rollback?: () => Promise<void>;
}
async function executeMigration(plan: MigrationPlan): Promise<string> {
console.log(`从 v${plan.sourceVersion} 迁移到 v${plan.targetVersion}`);
const completedSteps: MigrationStep[] = [];
try {
for (const step of plan.steps) {
console.log(`执行: ${step.name}`);
await step.execute();
completedSteps.push(step);
}
console.log('迁移成功完成');
return 'success';
} catch (error) {
console.error('迁移失败,正在回滚...');
// 按相反顺序回滚
for (const step of completedSteps.reverse()) {
if (step.rollback) {
try {
await step.rollback();
} catch (rollbackError) {
console.error(`${step.name} 回滚失败`);
}
}
}
throw error;
}
}
优雅弃用
async function deprecateContract(
oldContract: ConnectedContract,
newContractAddress: string
): Promise<void> {
// 1. 在旧合约中设置弃用标志(如果支持)
try {
await oldContract.call.set_deprecated({
successor: newContractAddress,
});
console.log('旧合约已标记为弃用');
} catch {
console.log('合约不支持弃用标志');
}
// 2. 记录弃用以供监控
console.log(`合约 ${oldContract.address} 已弃用`);
console.log(`后继者: ${newContractAddress}`);
// 3. 更新文档和客户端配置
// (手动步骤或与您的部署系统集成)
}
状态检查查询
常见状态检查操作:
// 获取所有账户及其余额
const accounts = await contract.query.get_all_accounts();
// 获取合约元数据
const metadata = await contract.query.get_metadata();
// 获取特定状态字段
const owner = await contract.query.get_owner();
// 获取特定区块的状态(通过索引器)
const historicalState = await indexer.getContractStateAtBlock(
CONTRACT_ADDRESS,
blockHeight
);
相关技能
contract-deployment- 部署新合约版本contract-calling- 与合约交互midnight-indexer插件 - 高级状态查询
相关命令
/midnight:check- 验证环境配置