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

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

安装

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

cargo add pallet-preimage

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

pallet-preimage = "42.0.0"

示例代码

下面是一个使用pallet-preimage实现链上数据预处理与存储功能的完整示例:

// 引入必要的依赖
use frame_support::{decl_module, decl_storage, decl_event, dispatch};
use frame_system::{self as system, ensure_signed};
use sp_std::prelude::*;

// 配置pallet-preimage的trait
pub trait Trait: system::Trait {
    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
}

// 声明存储项
decl_storage! {
    trait Store for Module<T: Trait> as PreimageStorage {
        // 存储预处理数据的哈希到原始数据的映射
        Preimages get(fn preimages): map hasher(blake2_128_concat) T::Hash => Option<Vec<u8>>;
    }
}

// 声明事件
decl_event!(
    pub enum Event<T> where <T as system::Trait>::Hash {
        // 当数据被存储时触发
        DataStored(Hash),
        // 当数据被处理时触发
        DataProcessed(Hash),
    }
);

// 声明模块
decl_module! {
    pub struct Module<T: Trait> for enum Call where origin: T::Origin {
        // 初始化事件
        fn deposit_event() = default;

        // 存储原始数据
        #[weight = 10_000]
        fn store_data(origin, data: Vec<u8>) -> dispatch::DispatchResult {
            // 验证签名
            let sender = ensure_signed(origin)?;
            
            // 计算数据的哈希
            let hash = T::Hashing::hash(&data);
            
            // 存储数据
            Preimages::<T>::insert(&hash, data);
            
            // 触发事件
            Self::deposit_event(RawEvent::DataStored(hash));
            
            Ok(())
        }

        // 预处理存储的数据
        #[weight = 10_000]
        fn process_data(origin, hash: T::Hash) -> dispatch::DispatchResult {
            // 验证签名
            let _ = ensure_signed(origin)?;
            
            // 获取存储的数据
            let data = Preimages::<T>::get(&hash).ok_or("Data not found")?;
            
            // 这里可以添加数据预处理逻辑
            // 例如: 数据验证、格式转换等
            
            // 触发事件
            Self::deposit_event(RawEvent::DataProcessed(hash));
            
            Ok(())
        }
    }
}

功能说明

  1. 数据存储:

    • 使用store_data函数将原始数据存储在链上
    • 数据以哈希值作为键存储在映射中
    • 触发DataStored事件
  2. 数据预处理:

    • 使用process_data函数处理已存储的数据
    • 通过哈希值检索数据
    • 可以添加自定义预处理逻辑
    • 触发DataProcessed事件
  3. 安全性:

    • 所有操作都需要签名验证
    • 使用哈希值确保数据完整性

集成到运行时

要将此pallet集成到Substrate运行时中,需要在construct_runtime!宏中添加它:

construct_runtime!(
    pub enum Runtime where
        Block = Block,
        NodeBlock = opaque::Block,
        UncheckedExtrinsic = UncheckedExtrinsic
    {
        // 其他pallet...
        Preimage: preimage::{Module, Call, Storage, Event<T>},
    }
);

完整示例DEMO

以下是一个完整的运行时集成示例,展示如何在Substrate链中使用预编译好的pallet-preimage:

// runtime/src/lib.rs

// 1. 引入必要的依赖
use frame_support::{construct_runtime, parameter_types};
use sp_core::H256;
use sp_runtime::{
    traits::{BlakeTwo256, IdentityLookup},
    ModuleId,
};

// 2. 配置运行时参数
parameter_types! {
    pub const PreimageModuleId: ModuleId = ModuleId(*b"py/preimg");
}

// 3. 实现pallet-preimage的Trait
impl preimage::Trait for Runtime {
    type Event = Event;
}

// 4. 构造运行时
construct_runtime!(
    pub enum Runtime where
        Block = Block,
        NodeBlock = opaque::Block,
        UncheckedExtrinsic = UncheckedExtrinsic
    {
        System: system::{Module, Call, Config, Storage, Event<T>},
        // 其他pallet...
        Preimage: preimage::{Module, Call, Storage, Event<T>},
    }
);

// 5. 实现链上数据处理的示例调用
fn example_usage() {
    // 创建测试数据
    let test_data = vec![1, 2, 3, 4, 5];
    
    // 存储数据到链上
    Preimage::store_data(Origin::signed(1), test_data.clone()).unwrap();
    
    // 计算数据哈希
    let hash = BlakeTwo256::hash(&test_data);
    
    // 处理数据
    Preimage::process_data(Origin::signed(1), hash).unwrap();
    
    // 验证数据存在
    assert!(Preimage::preimages(hash).is_some());
}

许可证

该项目使用Apache-2.0许可证。


1 回复

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

概述

pallet-preimage是Substrate区块链框架中的一个核心模块,用于实现链上数据的预处理和存储功能。它允许用户预先上传数据到链上,而不立即执行相关操作,这种机制在治理、升级和其他需要预先审核数据的场景中非常有用。

主要功能

  1. 数据预处理:允许用户预先上传数据到链上
  2. 哈希引用:使用数据的哈希作为引用,而不是直接存储原始数据
  3. 延迟执行:可以在未来某个时刻基于预存的数据执行操作
  4. 状态管理:跟踪预存数据的状态(未请求、已请求、可用)

完整示例代码

// 引入所需库
use frame_support::{dispatch::DispatchResult, traits::Currency};
use frame_system::Config as SystemConfig;
use sp_core::hashing::blake2_256;
use sp_std::vec::Vec;

/// 预存数据示例
pub fn store_preimage<T: pallet_preimage::Config>(
    sender: T::AccountId,
    data: Vec<u8>,
) -> DispatchResult {
    // 计算数据的Blake2b-256哈希
    let hash = blake2_256(&data);
    
    // 存储预存数据,需要支付押金
    pallet_preimage::Pallet::<T>::note_preimage(
        frame_system::RawOrigin::Signed(sender).into(),
        data,
    )?;
    
    Ok(())
}

/// 查询预存数据状态
pub fn check_preimage_status<T: pallet_preimage::Config>(
    hash: [u8; 32],
) -> Option<pallet_preimage::RequestStatus<<T as SystemConfig>::BlockNumber>> {
    pallet_preimage::Pallet::<T>::status(&hash)
}

/// 使用预存数据
pub fn use_preimage<T: pallet_preimage::Config>(
    hash: [u8; 32],
) -> Result<Vec<u8>, &'static str> {
    pallet_preimage::Pallet::<T>::fetch(&hash, None)
        .ok_or("Preimage not available")
}

/// 释放预存数据回收押金
pub fn remove_preimage<T: pallet_preimage::Config>(
    sender: T::AccountId,
    hash: [u8; 32],
) -> DispatchResult {
    pallet_preimage::Pallet::<T>::unnote_preimage(
        frame_system::RawOrigin::Signed(sender).into(),
        hash,
    )
}

/// 治理提案中使用预存数据的完整示例
pub fn governance_proposal_with_preimage<T: pallet_preimage::Config + pallet_democracy::Config>(
    proposer: T::AccountId,
    wasm_code: Vec<u8>,
    voting_period: T::BlockNumber,
) -> DispatchResult {
    // 1. 预存WASM运行时升级代码
    let hash = store_preimage::<T>(proposer.clone(), wasm_code)?;
    
    // 2. 创建引用该哈希的治理提案
    pallet_democracy::Pallet::<T>::propose(
        frame_system::RawOrigin::Signed(proposer).into(),
        Box::new(
            frame_system::Call::<T>::set_code {
                code: pallet_preimage::PreimageLookup::Unlooked(hash),
            }.into()
        ),
        voting_period,
    )?;
    
    Ok(())
}

/// 批量处理大数据的完整示例
pub fn process_large_data<T: pallet_preimage::Config>(
    sender: T::AccountId,
    large_data: Vec<u8>,
    chunk_size: usize,
) -> Result<Vec<[u8; 32]>, &'static str> {
    // 分割大数据为多个块
    let chunks = large_data.chunks(chunk_size)
        .map(|chunk| chunk.to_vec())
        .collect::<Vec<_>>();
    
    // 存储所有块并收集哈希
    let mut hashes = Vec::new();
    for chunk in chunks {
        let hash = blake2_256(&chunk);
        pallet_preimage::Pallet::<T>::note_preimage(
            frame_system::RawOrigin::Signed(sender.clone()).into(),
            chunk,
        ).map_err(|_| "Failed to note preimage")?;
        hashes.push(hash);
    }
    
    Ok(hashes)
}

/// 从多个哈希重建原始数据
pub fn reconstruct_data<T: pallet_preimage::Config>(
    hashes: Vec<[u8; 32]>,
) -> Result<Vec<u8>, &'static str> {
    let mut combined_data = Vec::new();
    
    for hash in hashes {
        let chunk = pallet_preimage::Pallet::<T>::fetch(&hash, None)
            .ok_or("Preimage chunk not found")?;
        combined_data.extend(chunk);
    }
    
    Ok(combined_data)
}

注意事项

  1. 存储成本:预存数据需要押金,计算方式为BaseDeposit + ByteDeposit * byte_length
  2. 哈希算法:默认使用Blake2b-256哈希算法
  3. 生命周期:预存数据在被使用后不会自动清除,需要手动调用unnote_preimage来回收押金
  4. 大小限制:单个预存数据的大小受区块大小限制

总结

pallet-preimage为Substrate链提供了一种高效的数据预处理机制,特别适合需要预先审核数据的场景,如运行时升级、治理提案等。通过将数据存储与数据使用分离,它提高了链上操作的灵活性和安全性。

回到顶部