Rust区块链开发库pallet-revive的使用,Substrate框架pallet-revive实现智能合约功能扩展与链上升级
Revive Pallet
这是一个实验性模块,为运行时提供部署和执行PolkaVM智能合约的功能。它是基于pallet_contracts
分支进行大量修改而来。
概述
该模块扩展了基于frame_support::traits::fungible
特性的账户,使其具备智能合约功能。这些"智能合约账户"可以:
- 实例化智能合约
- 调用其他合约和非合约账户
- 代码只需存储一次,通过
code_hash
检索 - 允许多个合约实例共享同一份代码
当账户被删除时,关联的智能合约代码和存储也会被清除。
权重机制
- 每次调用必须指定权重限制
- 未使用的权重会被退还
- 权重耗尽时,仅回滚当前合约级别的更改
ref_time
权重单位 = 1皮秒执行时间
错误处理
- 失败不会自动向上冒泡
- 调用者可以决定如何处理子调用失败
- 支持部分回滚(仅当前合约级别)
使用说明
支持编写编译为RISC-V的智能合约,目前官方支持:
- Solidity (通过revive项目)
- Rust (参考fixtures目录示例)
调试工具
可以通过设置日志级别跟踪主机函数调用:
cargo run --release -- --dev -lerror,runtime::revive::strace=trace,runtime::revive=debug
不稳定接口
- 类似Rust nightly的不稳定功能机制
- 生产环境应禁用(
UnsafeUnstableInterface = ConstU32<0>
) - 新功能先作为不稳定接口添加,经迭代后可能稳定
完整示例代码
// 运行时配置
impl pallet_revive::Config for Runtime {
type RuntimeEvent = RuntimeEvent; // 事件类型
type Currency = Balances; // 使用的货币类型
type WeightPrice = TransactionByteFee; // 权重价格
type WeightInfo = (); // 权重信息
type ChainExtension = (); // 链扩展
type UnsafeUnstableInterface = ConstU32<0>; // 生产环境禁用不稳定接口
type Migrations = (); // 迁移逻辑
}
// 合约部署示例
use pallet_revive::weights::SubstrateWeight;
use frame_support::weights::Weight;
// 准备部署参数
let gas_limit = Weight::from_parts(100_000, 0); // 设置gas限制
let code = include_bytes!("./contract.wasm").to_vec(); // 加载wasm合约代码
let salt = b"unique_salt".to_vec(); // 部署盐值
let value = 100; // 转账金额
// 部署新合约
let deploy_result = PalletRevive::instantiate_with_code(
RuntimeOrigin::signed(caller), // 调用者
gas_limit, // gas限制
None, // 不限制存储押金
code, // 合约代码
None, // 无初始化数据
salt, // 盐值
value, // 转账金额
);
// 调用已部署合约
let contract_address = deploy_result.unwrap().account_id; // 获取合约地址
let input_data = b"function_call".to_vec(); // 调用数据
let call_result = PalletRevive::call(
RuntimeOrigin::signed(caller), // 调用者
contract_address.clone(), // 合约地址
gas_limit, // gas限制
None, // 不限制存储押金
input_data, // 调用数据
value, // 转账金额
);
许可证: Apache-2.0
1 回复
Rust区块链开发库pallet-revive的使用 - Substrate框架实现智能合约功能扩展与链上升级
完整示例代码
下面是一个完整的pallet-revive使用示例,包含合约部署、升级和状态迁移的全流程:
// 1. 运行时集成(runtime/src/lib.rs)
pub mod runtime {
use frame_support::{construct_runtime, parameter_types};
use sp_core::sr25519::Signature;
use sp_runtime::{
generic,
traits::{BlakeTwo256, IdentifyAccount, Verify},
};
// 定义运行时配置
pub type BlockNumber = u32;
pub type AccountId = <<Signature as Verify>::Signer as IdentifyAccount>::AccountId;
pub type Balance = u128;
pub type Index = u32;
pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
pub type Block = generic::Block<Header, UncheckedExtrinsic>;
pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<Address, Call, Signature, SignedExtra>;
// 实现pallet-revive配置
impl pallet_revive::Config for Runtime {
type Event = Event;
type WeightInfo = pallet_revive::weights::SubstrateWeight<Runtime>;
type AdminOrigin = frame_system::EnsureRoot<AccountId>; // 根账户才有升级权限
}
construct_runtime!(
pub enum Runtime where
Block = Block,
NodeBlock = opaque::Block,
UncheckedExtrinsic = UncheckedExtrinsic
{
System: frame_system,
Timestamp: pallet_timestamp,
Balances: pallet_balances,
Revive: pallet_revive,
}
);
}
// 2. 合约部署示例(合约部署脚本)
fn deploy_sample_contract() {
use frame_support::sp_std::vec;
use pallet_revive::Pallet as Revive;
use sp_runtime::traits::AccountIdConversion;
// 获取部署者账户
let deployer = AccountId::from([1u8; 32]);
// 模拟WASM合约代码
let contract_code = vec![0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00]; // 简化的WASM字节码
// 部署新合约
let salt = b"my_contract_v1".to_vec();
Revive::deploy(
RuntimeOrigin::signed(deployer.clone()),
contract_code,
salt,
Default::default(),
).unwrap();
// 获取合约地址
let contract_address = Revive::contract_address(&deployer, &b"my_contract_v1".to_vec());
println!("Contract deployed at: {:?}", contract_address);
}
// 3. 合约升级示例
fn upgrade_contract_example() {
use pallet_revive::Pallet as Revive;
// 管理员账户
let admin = AccountId::from([0u8; 32]); // 根账户
// 新版本合约代码
let new_code = vec![0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00, 0x01]; // 更新的WASM字节码
// 假设我们要升级的合约地址
let contract_address = AccountId::from([2u8; 32]);
// 执行升级
Revive::upgrade(
RuntimeOrigin::signed(admin),
contract_address,
new_code,
Default::default(),
).unwrap();
}
// 4. 状态迁移示例
mod state_migration {
use frame_support::{codec::{Encode, Decode}, RuntimeDebug};
use pallet_revive::Pallet as Revive;
// 旧状态结构
#[derive(Encode, Decode, RuntimeDebug, Clone, PartialEq)]
pub struct OldState {
pub balance: u64,
pub owner: AccountId,
}
// 新状态结构
#[derive(Encode, Decode, RuntimeDebug, Clone, PartialEq)]
pub struct NewState {
pub balance: u128, // 从u64升级到u128
pub owner: AccountId,
pub is_active: bool, // 新增字段
}
// 状态迁移函数
pub fn migrate_state(old: OldState) -> NewState {
NewState {
balance: old.balance as u128,
owner: old.owner,
is_active: true, // 默认激活新合约
}
}
// 注册状态迁移
pub fn register_migration(admin: AccountId, contract_address: AccountId) {
Revive::register_migration(
RuntimeOrigin::signed(admin),
contract_address,
migrate_state,
).unwrap();
}
}
// 5. 多阶段升级示例
fn multi_phase_upgrade() {
use sp_core::H256;
use pallet_revive::Pallet as Revive;
let admin = AccountId::from([0u8; 32]);
let contract_address = AccountId::from([2u8; 32]);
let new_code_hash = H256::from([1u8; 32]); // 模拟新代码哈希
// 阶段1: 准备升级
Revive::prepare_upgrade(
RuntimeOrigin::signed(admin.clone()),
contract_address.clone(),
new_code_hash,
).unwrap();
// 阶段2: 执行升级
Revive::commit_upgrade(
RuntimeOrigin::signed(admin),
contract_address,
).unwrap();
}
// 6. 权限控制配置示例
mod custom_admin {
use frame_support::traits::Contains;
// 自定义管理员组
pub struct AdminCouncil;
impl Contains<AccountId> for AdminCouncil {
fn contains(a: &AccountId) -> bool {
// 实际项目中这里会有更复杂的逻辑检查
a.as_ref()[0] == 0xFF // 简单示例:地址以0xFF开头的是管理员
}
}
// 使用自定义权限配置
pub fn configure_runtime() {
impl pallet_revive::Config for Runtime {
type AdminOrigin = frame_system::EnsureSignedBy<AdminCouncil, AccountId>;
// ... 其他配置保持不变
}
}
}
代码说明
-
运行时集成:
- 在Substrate运行时中正确配置pallet-revive
- 设置适当的权限控制(示例中使用root账户作为管理员)
-
合约部署:
- 演示如何使用WASM字节码部署新合约
- 使用salt参数确保合约地址确定性
-
合约升级:
- 展示如何用新代码替换现有合约逻辑
- 需要管理员权限才能执行
-
状态迁移:
- 定义旧状态和新状态的数据结构
- 实现状态转换函数并注册到pallet-revive
-
多阶段升级:
- 分准备和执行两个阶段完成升级
- 适合需要协调的复杂升级场景
-
自定义权限:
- 展示如何配置非root账户的管理权限
- 可以实现更复杂的治理模型
使用建议
- 在实际部署前,务必在测试网验证所有升级流程
- 对于生产环境,建议使用多签账户作为管理员
- 状态迁移函数应进行充分测试,确保数据一致性
- 大型升级可以考虑分批次执行,减少对网络的影响
这个完整示例展示了pallet-revive从基础到高级的用法,开发者可以根据实际需求调整配置和调用方式。