Rust Substrate框架pallet-parameters的使用:实现链上可配置参数的动态管理与存储优化
Rust Substrate框架pallet-parameters的使用:实现链上可配置参数的动态管理与存储优化
安装
在项目目录中运行以下Cargo命令:
cargo add pallet-parameters
或者在Cargo.toml中添加以下行:
pallet-parameters = "0.12.0"
示例代码
以下是使用pallet-parameters实现链上可配置参数的完整示例:
// 在runtime/src/lib.rs中配置pallet
// 1. 引入pallet_parameters
pub use pallet_parameters;
// 2. 配置trait
impl pallet_parameters::Config for Runtime {
type Event = Event;
type WeightInfo = ();
}
// 3. 将pallet添加到construct_runtime宏中
construct_runtime!(
pub enum Runtime where
Block = Block,
NodeBlock = opaque::Block,
UncheckedExtrinsic = UncheckedExtrinsic
{
// ...其他pallet
Parameters: pallet_parameters::{Module, Call, Storage, Event<T>},
}
);
// 在pallet中使用参数的示例
#[frame_support::pallet]
pub mod pallet_example {
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;
#[pallet::config]
pub trait Config: frame_system::Config + pallet_parameters::Config {
type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
}
#[pallet::pallet]
#[pallet::generate_store(pub(super) trait Store)]
pub struct Pallet<T>(_);
#[pallet::storage]
#[pallet::getter(fn some_value)]
pub type SomeValue<T: Config> = StorageValue<_, u32, ValueQuery>;
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
ParameterUpdated(u32),
}
#[pallet::call]
impl<T: Config> Pallet<T> {
#[pallet::weight(10_000)]
pub fn update_parameter(
origin: OriginFor<T>,
new_value: u32,
) -> DispatchResult {
let _ = ensure_signed(origin)?;
// 使用pallet-parameters存储参数
pallet_parameters::Pallet::<T>::set_parameter(
b"example_parameter",
&new_value,
)?;
// 也可以存储在自己的pallet中
<SomeValue<T>>::put(new_value);
Self::deposit_event(Event::ParameterUpdated(new_value));
Ok(())
}
#[pallet::weight(10_000)]
pub fn get_parameter(
origin: OriginFor<T>,
) -> DispatchResultWithPostInfo {
let _ = ensure_signed(origin)?;
// 从pallet-parameters获取参数
let param: u32 = pallet_parameters::Pallet::<T>::get_parameter(
b"example_parameter"
)?;
log::info!("Current parameter value: {}", param);
Ok(().into())
}
}
}
完整示例demo
以下是基于上述内容的完整示例实现,展示了如何使用pallet-parameters进行链上参数管理:
// runtime/src/lib.rs
pub mod runtime {
// ...其他引入
// 引入pallet_parameters
pub use pallet_parameters;
// 配置pallet_parameters的trait
impl pallet_parameters::Config for Runtime {
type Event = Event;
type WeightInfo = pallet_parameters::weights::SubstrateWeight<Runtime>;
}
// 在construct_runtime!宏中添加pallet
construct_runtime!(
pub enum Runtime where
Block = Block,
NodeBlock = opaque::Block,
UncheckedExtrinsic = UncheckedExtrinsic
{
System: frame_system,
// ...其他pallets
Parameters: pallet_parameters::{Module, Call, Storage, Event<T>},
ExamplePallet: pallet_example,
}
);
}
// pallets/example/src/lib.rs
#[frame_support::pallet]
pub mod pallet_example {
use frame_support::{pallet_prelude::*, traits::Get};
use frame_system::pallet_prelude::*;
#[pallet::config]
pub trait Config: frame_system::Config + pallet_parameters::Config {
type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
}
#[pallet::pallet]
#[pallet::generate_store(pub(super) trait Store)]
pub struct Pallet<T>(_);
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
/// 参数已更新
ParameterUpdated { key: Vec<u8>, value: Vec<u8> },
/// 参数已读取
ParameterRead { key: Vec<u8>, value: Vec<u8> },
}
#[pallet::error]
pub enum Error<T> {
/// 参数不存在
ParameterNotFound,
/// 参数类型不匹配
ParameterTypeMismatch,
}
#[pallet::call]
impl<T: Config> Pallet<T> {
/// 更新链上参数
#[pallet::weight(10_000)]
pub fn update_parameter(
origin: OriginFor<T>,
key: Vec<u8>, // 参数键
value: Vec<u8>, // 参数值
) -> DispatchResult {
// 验证调用者权限
ensure_root(origin)?;
// 存储参数到pallet-parameters
pallet_parameters::Pallet::<T>::set_parameter(&key, &value)?;
// 发出事件
Self::deposit_event(Event::ParameterUpdated {
key: key.clone(),
value: value.clone(),
});
Ok(())
}
/// 获取链上参数
#[pallet::weight(10_000)]
pub fn get_parameter(
origin: OriginFor<T>,
key: Vec<u8>, // 参数键
) -> DispatchResultWithPostInfo {
// 验证调用者权限
let _ = ensure_signed(origin)?;
// 从pallet-parameters获取参数
let value = pallet_parameters::Pallet::<T>::get_parameter(&key)
.map_err(|_| Error::<T>::ParameterNotFound)?;
// 发出事件
Self::deposit_event(Event::ParameterRead {
key: key.clone(),
value: value.clone(),
});
log::info!("Parameter {}: {:?}", String::from_utf8_lossy(&key), value);
Ok(().into())
}
/// 获取并解析特定类型的参数
#[pallet::weight(10_000)]
pub fn get_typed_parameter(
origin: OriginFor<T>,
key: Vec<u8>, // 参数键
) -> DispatchResultWithPostInfo {
// 验证调用者权限
let _ = ensure_signed(origin)?;
// 尝试获取u32类型的参数
let value: u32 = pallet_parameters::Pallet::<T>::get_parameter(&key)
.map_err(|_| Error::<T>::ParameterNotFound)?;
log::info!("Typed parameter {}: {}", String::from_utf8_lossy(&key), value);
Ok(().into())
}
}
}
关键特性
-
动态参数管理:
- 使用
set_parameter
和get_parameter
方法动态管理链上参数 - 参数可以随时更新,无需硬编码或重新部署
- 使用
-
存储优化:
- pallet-parameters提供了高效的存储结构
- 参数以键值对形式存储,优化存储空间使用
-
类型安全:
- 使用Rust的类型系统确保参数类型安全
- 自动序列化/反序列化
-
权限控制:
- 可以通过Origin控制谁可以修改参数
- 集成Substrate的权限管理系统
最佳实践
- 为参数使用有意义的键名
- 考虑参数的大小和存储成本
- 实现适当的权限控制
- 记录参数变更事件
- 考虑参数的版本控制
通过pallet-parameters,开发者可以轻松实现链上可配置参数,提高区块链应用的灵活性和可维护性。
Rust Substrate框架pallet-parameters的使用:实现链上可配置参数的动态管理与存储优化
介绍
pallet-parameters
是Substrate框架中的一个模块,专门用于实现区块链上的参数动态管理和存储优化。它允许链上参数在运行时(runtime)进行配置和更新,而无需硬分叉或重新编译整个区块链。
这个pallet特别适合需要频繁调整参数的应用场景,如治理系统、DeFi协议参数调整或网络费用变更等。
主要功能
- 提供链上参数的声明式定义
- 支持参数的动态更新
- 优化参数的存储方式
- 提供参数变更的事件通知
- 支持参数访问权限控制
使用方法
1. 在runtime中集成pallet-parameters
首先需要在runtime的Cargo.toml
中添加依赖:
[dependencies]
pallet-parameters = { git = "https://github.com/paritytech/substrate.git", branch = "master", default-features = false }
然后在runtime的lib.rs中配置和实现:
impl pallet_parameters::Config for Runtime {
type Event = Event;
type WeightInfo = ();
}
construct_runtime!(
pub enum Runtime where
Block = Block,
NodeBlock = opaque::Block,
UncheckedExtrinsic = UncheckedExtrinsic
{
// ... 其他pallet
Parameters: pallet_parameters::{Module, Call, Storage, Event<T>},
}
);
2. 定义可配置参数
在runtime中定义需要动态管理的参数:
#[frame_support::pallet]
pub mod pallet {
use frame_support::pallet_prelude::*;
use frame system::pallet_prelude::*;
#[pallet::config]
pub trait Config: frame_system::Config {
// ... 其他配置
#[pallet::constant]
type MaxVotes: Get<u32>;
}
#[pallet::storage]
#[pallet::getter(fn parameters)]
pub type Parameters<T: Config> = StorageMap<
_,
Blake2_128Concat,
Vec<u8>,
Vec<u8>,
ValueQuery
>;
}
3. 设置和获取参数
设置参数
use frame_support::{dispatch::DispatchResult, storage::StorageMap};
pub fn set_parameter(key: Vec<u8>, value: Vec<u8>) -> DispatchResult {
Parameters::<T>::insert(key, value);
Ok(())
}
获取参数
pub fn get_parameter(key: Vec<u8>) -> Option<Vec<u8>> {
Parameters::<T>::get(key)
}
4. 完整示例
下面是一个完整的pallet示例,展示如何定义和使用可配置参数:
#![cfg_attr(not(feature = "std"), no_std)]
pub use pallet::*;
#[frame_support::pallet]
pub mod pallet {
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;
#[pallet::config]
pub trait Config: frame_system::Config {
type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
/// 谁可以设置参数
type AdminOrigin: EnsureOrigin<Self::Origin>;
}
#[pallet::pallet]
#[pallet::generate_store(pub(super) trait Store)]
pub struct Pallet<T>(_);
#[pallet::storage]
#[pallet::getter(fn parameters)]
pub type Parameters<T: Config] = StorageMap<
_,
Blake2_128Concat,
Vec<u8>, // 参数名
Vec<u8>, // 参数值
ValueQuery
>;
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
/// 参数已更新
ParameterUpdated { key: Vec<u8>, value: Vec<u8> },
}
#[pallet::error]
pub enum Error<T> {
/// 参数不存在
ParameterNotFound,
}
#[pallet::call]
impl<T: Config] Pallet<T> {
/// 设置参数
#[pallet::weight(10_000)]
pub fn set_parameter(
origin: OriginFor<T>,
key: Vec<u8>,
value: Vec<u8>,
) -> DispatchResultWithPostInfo {
T::AdminOrigin::ensure_origin(origin)?;
Parameters::<T>::insert(&key, &value);
Self::deposit_event(Event::ParameterUpdated { key, value });
Ok(().into())
}
/// 获取参数
#[pallet::weight(10_000)]
pub fn get_parameter(
_origin: OriginFor<T>,
key: Vec<u8>,
) -> DispatchResultWithPostInfo {
let value = Parameters::<T>::get(&key)
.ok_or(Error::<T>::ParameterNotFound)?;
Ok(().into())
}
}
}
5. 使用宏简化参数定义
Substrate提供了parameter_types!
宏来简化参数定义:
parameter_types! {
pub const MaxVotes: u32 = 100;
pub const VotingPeriod: u32 = 7 * 24 * 60 * 60; // 7天(秒)
pub const MinimumDeposit: Balance = 100 * DOLLARS;
}
6. 存储优化技巧
为了优化存储,可以考虑以下方法:
- 使用紧凑编码格式
- 合并相关参数
- 使用默认值减少存储
#[pallet::storage]
#[pallet::getter(fn config)]
pub type Config<T> = StorageValue<_, CompactConfig, ValueQuery>;
#[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, TypeInfo)]
#[scale_info(skip_type_params(T))]
pub struct CompactConfig {
pub max_votes: u32,
pub voting_period: u32,
pub min_deposit: Balance,
}
impl Default for CompactConfig {
fn default() -> Self {
CompactConfig {
max_votes: 100,
voting_period: 7 * 24 * 60 * 60,
min_deposit: 100 * DOLLARS,
}
}
}
最佳实践
- 参数分组:将相关参数组合在一起,减少存储访问次数
- 访问控制:严格限制参数修改权限
- 变更通知:确保参数变更时发出事件
- 版本控制:考虑为参数结构添加版本号
- 文档化:为每个参数添加详细文档说明
完整示例demo
以下是一个更完整的示例,展示如何在实际项目中使用pallet-parameters:
#![cfg_attr(not(feature = "std"), no_std)]
use frame_support::{
dispatch::DispatchResult,
pallet_prelude::*,
traits::EnsureOrigin,
weights::Weight,
};
use frame_system::pallet_prelude::*;
pub use pallet::*;
#[frame_support::pallet]
pub mod pallet {
use super::*;
/// 参数模块配置trait
#[pallet::config]
pub trait Config: frame_system::Config {
/// 事件类型
type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
/// 管理员权限
type AdminOrigin: EnsureOrigin<Self::Origin>;
/// 权重信息
type WeightInfo: WeightInfo;
}
/// 权重信息接口
pub trait WeightInfo {
fn set_parameter() -> Weight;
fn get_parameter() -> Weight;
}
#[pallet::pallet]
#[pallet::generate_store(pub(super) trait Store)]
pub struct Pallet<T>(_);
/// 参数存储
#[pallet::storage]
#[pallet::getter(fn parameters)]
pub type Parameters<T: Config> = StorageMap<
_,
Blake2_128Concat,
Vec<u8>, // 参数键
Vec<u8>, // 参数值
ValueQuery
>;
/// 参数模块事件
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
/// 参数已更新事件
ParameterUpdated { key: Vec<u8>, value: Vec<u8> },
}
/// 错误类型
#[pallet::error]
pub enum Error<T> {
/// 参数不存在
ParameterNotFound,
/// 参数格式无效
InvalidParameterFormat,
}
#[pallet::call]
impl<T: Config> Pallet<T> {
/// 设置参数
#[pallet::weight(T::WeightInfo::set_parameter())]
pub fn set_parameter(
origin: OriginFor<T>,
key: Vec<u8>,
value: Vec<u8>,
) -> DispatchResultWithPostInfo {
// 验证调用者权限
T::AdminOrigin::ensure_origin(origin)?;
// 验证参数格式
if key.is_empty() || value.is_empty() {
return Err(Error::<T>::InvalidParameterFormat.into());
}
// 存储参数
Parameters::<T>::insert(&key, &value);
// 发出事件
Self::deposit_event(Event::ParameterUpdated { key, value });
Ok(().into())
}
/// 获取参数
#[pallet::weight(T::WeightInfo::get_parameter())]
pub fn get_parameter(
_origin: OriginFor<T>,
key: Vec<u8>,
) -> DispatchResultWithPostInfo {
// 获取参数
let _value = Parameters::<T>::get(&key)
.ok_or(Error::<T>::ParameterNotFound)?;
Ok(().into())
}
}
/// 实现默认权重
impl<T: Config> WeightInfo for Pallet<T> {
fn set_parameter() -> Weight {
Weight::from_ref_time(10_000)
}
fn get_parameter() -> Weight {
Weight::from_ref_time(5_000)
}
}
}
/// 测试模块
#[cfg(test)]
mod tests {
use super::*;
use frame_support::{
assert_ok, assert_err,
traits::{ConstU32, ConstU64},
};
use sp_core::H256;
use sp_runtime::{
traits::{BlakeTwo256, IdentityLookup},
BuildStorage,
};
type Block = frame_system::mocking::MockBlock<Test>;
frame_support::construct_runtime!(
pub enum Test where
Block = Block,
NodeBlock = Block,
UncheckedExtrinsic = (),
{
System: frame_system,
Parameters: pallet,
}
);
impl frame_system::Config for Test {
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 = u64;
type Lookup = IdentityLookup<Self::AccountId>;
type Header = sp_runtime::testing::Header;
type RuntimeEvent = RuntimeEvent;
type BlockHashCount = ConstU64<250>;
type Version = ();
type PalletInfo = PalletInfo;
type AccountData = ();
type OnNewAccount = ();
type OnKilledAccount = ();
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = ConstU32<16>;
}
impl Config for Test {
type Event = RuntimeEvent;
type AdminOrigin = frame_system::EnsureRoot<u64>;
type WeightInfo = ();
}
fn new_test_ext() -> sp_io::TestExternalities {
let t = frame_system::GenesisConfig::<Test>::default()
.build_storage()
.unwrap();
t.into()
}
#[test]
fn test_set_and_get_parameter() {
new_test_ext().execute_with(|| {
let key = b"test_key".to_vec();
let value = b"test_value".to_vec();
// 设置参数
assert_ok!(Parameters::set_parameter(
RuntimeOrigin::root(),
key.clone(),
value.clone()
));
// 获取参数
assert_eq!(Parameters::parameters(key), value);
});
}
}
总结
pallet-parameters
为Substrate区块链提供了灵活的参数管理系统,使链上配置可以在运行时动态调整,大大提高了区块链的适应性和可维护性。通过合理的设计和优化,可以在保持灵活性的同时最小化存储开销。