Rust区块链开发库pallet-revive的使用,Substrate框架pallet-revive实现智能合约功能扩展与链上升级

Revive Pallet

这是一个实验性模块,为运行时提供部署和执行PolkaVM智能合约的功能。它是基于pallet_contracts分支进行大量修改而来。

概述

该模块扩展了基于frame_support::traits::fungible特性的账户,使其具备智能合约功能。这些"智能合约账户"可以:

  1. 实例化智能合约
  2. 调用其他合约和非合约账户
  3. 代码只需存储一次,通过code_hash检索
  4. 允许多个合约实例共享同一份代码

当账户被删除时,关联的智能合约代码和存储也会被清除。

权重机制

  • 每次调用必须指定权重限制
  • 未使用的权重会被退还
  • 权重耗尽时,仅回滚当前合约级别的更改
  • ref_time权重单位 = 1皮秒执行时间

错误处理

  • 失败不会自动向上冒泡
  • 调用者可以决定如何处理子调用失败
  • 支持部分回滚(仅当前合约级别)

使用说明

支持编写编译为RISC-V的智能合约,目前官方支持:

  1. Solidity (通过revive项目)
  2. 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>;
            // ... 其他配置保持不变
        }
    }
}

代码说明

  1. 运行时集成

    • 在Substrate运行时中正确配置pallet-revive
    • 设置适当的权限控制(示例中使用root账户作为管理员)
  2. 合约部署

    • 演示如何使用WASM字节码部署新合约
    • 使用salt参数确保合约地址确定性
  3. 合约升级

    • 展示如何用新代码替换现有合约逻辑
    • 需要管理员权限才能执行
  4. 状态迁移

    • 定义旧状态和新状态的数据结构
    • 实现状态转换函数并注册到pallet-revive
  5. 多阶段升级

    • 分准备和执行两个阶段完成升级
    • 适合需要协调的复杂升级场景
  6. 自定义权限

    • 展示如何配置非root账户的管理权限
    • 可以实现更复杂的治理模型

使用建议

  1. 在实际部署前,务必在测试网验证所有升级流程
  2. 对于生产环境,建议使用多签账户作为管理员
  3. 状态迁移函数应进行充分测试,确保数据一致性
  4. 大型升级可以考虑分批次执行,减少对网络的影响

这个完整示例展示了pallet-revive从基础到高级的用法,开发者可以根据实际需求调整配置和调用方式。

回到顶部