Rust Substrate区块链框架pallet-preimage插件库的使用,实现链上数据预处理与存储功能

Rust Substrate区块链框架pallet-preimage插件库的使用,实现链上数据预处理与存储功能

安装

在项目目录中运行以下Cargo命令:

cargo add pallet-preimage

或者在Cargo.toml中添加以下行:

pallet-preimage = "42.0.0"

使用示例

pallet-preimage是Substrate框架中的一个重要组件,用于将数据预处理并存储在链上,以便后续使用。以下是完整的使用示例:

// 在runtime/src/lib.rs中配置pallet-preimage

use frame_support::traits::PreimageProvider;
use pallet_preimage::{Config, Pallet};

// 配置trait实现
pub trait Config: frame_system::Config {
    type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
    type Currency: ReservableCurrency<Self::AccountId>;
    type ManagerOrigin: EnsureOrigin<Self::RuntimeOrigin>;
    type WeightInfo: WeightInfo;
}

// 在construct_runtime!宏中添加pallet
construct_runtime!(
    pub enum Runtime where
        Block = Block,
        NodeBlock = opaque::Block,
        UncheckedExtrinsic = UncheckedExtrinsic,
    {
        // ...其他pallet...
        Preimage: pallet_preimage::{Pallet, Call, Storage, Event<T>},
    }
);

// 示例使用方法
impl pallet_preimage::Config for Runtime {
    type RuntimeEvent = RuntimeEvent;
    type Currency = Balances;
    type ManagerOrigin = EnsureRoot<AccountId>;
    type WeightInfo = pallet_preimage::weights::SubstrateWeight<Runtime>;
}

主要功能实现

// 存储预处理数据
let data = vec![1, 2, 3, 4, 5]; // 需要存储的数据
let hash = sp_io::hashing::blake2_256(&data); // 计算数据的哈希

// 提交数据到链上
pallet_preimage::Pallet::<Runtime>::note_preimage(
    RuntimeOrigin::signed(caller),
    data.clone(),
)?;

// 检查数据是否已存储
assert!(pallet_preimage::Pallet::<Runtime>::preimage(hash).is_some());

// 使用存储的数据
let retrieved_data = pallet_preimage::Pallet::<Runtime>::preimage(hash)
    .expect("数据应该存在");
assert_eq!(retrieved_data, data);

// 清理不再需要的数据
pallet_preimage::Pallet::<Runtime>::unnote_preimage(
    RuntimeOrigin::signed(caller),
    hash.into(),
)?;

完整示例demo

以下是一个完整的pallet-preimage使用示例,展示了从配置到实际使用的全过程:

// runtime/src/lib.rs

use frame_support::{
    construct_runtime, parameter_types,
    traits::{Currency, ReservableCurrency},
    weights::Weight,
};
use sp_core::H256;
use sp_runtime::{
    traits::{BlakeTwo256, IdentityLookup},
    AccountId32, Perbill,
};

// 基础配置
type Block = frame_system::mocking::MockBlock<Runtime>;
type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Runtime>;

// 实现Runtime配置
impl frame_system::Config for Runtime {
    type BaseCallFilter = frame_support::traits::Everything;
    type BlockWeights = ();
    type BlockLength = ();
    type DbWeight = ();
    type RuntimeOrigin = RuntimeOrigin;
    type RuntimeCall = RuntimeCall;
    type Index = u64;
    type BlockNumber = u64;
    type Hash = H256;
    type Hashing = BlakeTwo256;
    type AccountId = AccountId32;
    type Lookup = IdentityLookup<Self::AccountId>;
    type RuntimeEvent = RuntimeEvent;
    type BlockHashCount = BlockHashCount;
    type Version = ();
    type PalletInfo = PalletInfo;
    type AccountData = pallet_balances::AccountData<Balance>;
    type OnNewAccount = ();
    type OnKilledAccount = ();
    type SystemWeightInfo = ();
    type SS58Prefix = ();
    type OnSetCode = ();
    type MaxConsumers = frame_support::traits::ConstU32<16>;
}

// 预定义类型
type Balance = u128;

// 实现pallet_balances配置
impl pallet_balances::Config for Runtime {
    type Balance = Balance;
    type DustRemoval = ();
    type RuntimeEvent = RuntimeEvent;
    type ExistentialDeposit = ExistentialDeposit;
    type AccountStore = System;
    type WeightInfo = ();
    type MaxLocks = ();
    type MaxReserves = ();
    type ReserveIdentifier = [u8; 8];
    type FreezeIdentifier = ();
    type MaxFreezes = ();
    type HoldIdentifier = ();
    type MaxHolds = ();
}

// 实现pallet_preimage配置
impl pallet_preimage::Config for Runtime {
    type RuntimeEvent = RuntimeEvent;
    type Currency = Balances;
    type ManagerOrigin = frame_system::EnsureRoot<AccountId32>;
    type WeightInfo = ();
}

// 构建Runtime
construct_runtime!(
    pub enum Runtime where
        Block = Block,
        NodeBlock = Block,
        UncheckedExtrinsic = UncheckedExtrinsic,
    {
        System: frame_system,
        Balances: pallet_balances,
        Preimage: pallet_preimage,
    }
);

// 测试用例
#[test]
fn test_preimage_workflow() {
    use frame_support::assert_ok;
    use pallet_preimage::Call as PreimageCall;
    
    // 初始化测试环境
    let mut t = frame_system::GenesisConfig::default()
        .build_storage::<Runtime>()
        .unwrap();
    
    // 设置初始余额
    pallet_balances::GenesisConfig::<Runtime> {
        balances: vec![(1, 1000)],
    }
    .assimilate_storage(&mut t)
    .unwrap();
    
    let mut ext = sp_io::TestExternalities::new(t);
    ext.execute_with(|| {
        // 测试数据
        let data = vec![1, 2, 3, 4, 5];
        let hash = sp_io::hashing::blake2_256(&data);
        
        // 测试note_preimage
        assert_ok!(Preimage::note_preimage(
            RuntimeOrigin::signed(1),
            data.clone()
        ));
        
        // 验证数据存储
        assert!(Preimage::preimage(hash).is_some());
        let stored = Preimage::preimage(hash).unwrap();
        assert_eq!(stored, data);
        
        // 测试unnote_preimage
        assert_ok!(Preimage::unnote_preimage(
            RuntimeOrigin::signed(1),
            hash.into()
        ));
        
        // 验证数据已删除
        assert!(Preimage::preimage(hash).is_none());
    });
}

关键点说明

  1. 数据预处理:数据在上链前会计算哈希,哈希作为数据的唯一标识
  2. 存储机制:数据实际存储在链上,可以通过哈希查询
  3. 费用处理:存储数据需要支付费用,费用与数据大小相关
  4. 生命周期管理:可以通过unnote_preimage清理不再需要的数据

注意事项

  • 数据存储需要支付相应的存储费用
  • 大尺寸数据不适合直接存储在链上
  • 需要合理管理数据生命周期,避免不必要的存储开销

pallet-preimage为Substrate区块链提供了灵活的数据预处理和存储能力,是构建复杂链上逻辑的重要基础组件。


回到顶部