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(())
}
}
}
功能说明
-
数据存储:
- 使用
store_data
函数将原始数据存储在链上 - 数据以哈希值作为键存储在映射中
- 触发
DataStored
事件
- 使用
-
数据预处理:
- 使用
process_data
函数处理已存储的数据 - 通过哈希值检索数据
- 可以添加自定义预处理逻辑
- 触发
DataProcessed
事件
- 使用
-
安全性:
- 所有操作都需要签名验证
- 使用哈希值确保数据完整性
集成到运行时
要将此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区块链框架中的一个核心模块,用于实现链上数据的预处理和存储功能。它允许用户预先上传数据到链上,而不立即执行相关操作,这种机制在治理、升级和其他需要预先审核数据的场景中非常有用。
主要功能
- 数据预处理:允许用户预先上传数据到链上
- 哈希引用:使用数据的哈希作为引用,而不是直接存储原始数据
- 延迟执行:可以在未来某个时刻基于预存的数据执行操作
- 状态管理:跟踪预存数据的状态(未请求、已请求、可用)
完整示例代码
// 引入所需库
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)
}
注意事项
- 存储成本:预存数据需要押金,计算方式为
BaseDeposit + ByteDeposit * byte_length
- 哈希算法:默认使用Blake2b-256哈希算法
- 生命周期:预存数据在被使用后不会自动清除,需要手动调用
unnote_preimage
来回收押金 - 大小限制:单个预存数据的大小受区块大小限制
总结
pallet-preimage
为Substrate链提供了一种高效的数据预处理机制,特别适合需要预先审核数据的场景,如运行时升级、治理提案等。通过将数据存储与数据使用分离,它提高了链上操作的灵活性和安全性。