Files
pezkuwi-sdk/bizinikiwi/pezframe/parameters/src/lib.rs
T
pezkuwichain b6d35f6faf chore: add Dijital Kurdistan Tech Institute to copyright headers
Updated 4763 files with dual copyright:
- Parity Technologies (UK) Ltd.
- Dijital Kurdistan Tech Institute
2025-12-27 21:28:36 +03:00

273 lines
10 KiB
Rust

// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd. and Dijital Kurdistan Tech Institute
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#![cfg_attr(not(feature = "std"), no_std)]
#![deny(missing_docs)]
// Need to enable this one since we document feature-gated stuff.
#![allow(rustdoc::broken_intra_doc_links)]
//! # **⚠️ WARNING ⚠️**
//!
//! <br>
//! <b>THIS CRATE IS NOT AUDITED AND SHOULD NOT BE USED IN PRODUCTION.</b>
//! <br>
//!
//! # Parameters
//!
//! Allows to update configuration parameters at runtime.
//!
//! ## Pezpallet API
//!
//! This pezpallet exposes two APIs; one *inbound* side to update parameters, and one *outbound*
//! side to access said parameters. Parameters themselves are defined in the runtime config and will
//! be aggregated into an enum. Each parameter is addressed by a `key` and can have a default value.
//! This is not done by the pezpallet but through the
//! [`pezframe_support::dynamic_params::dynamic_params`] macro or alternatives.
//!
//! Note that this is incurring one storage read per access. This should not be a problem in most
//! cases but must be considered in weight-restrained code.
//!
//! ### Inbound
//!
//! The inbound side solely consists of the [`Pezpallet::set_parameter`] extrinsic to update the
//! value of a parameter. Each parameter can have their own admin origin as given by the
//! [`Config::AdminOrigin`].
//!
//! ### Outbound
//!
//! The outbound side is runtime facing for the most part. More general, it provides a `Get`
//! implementation and can be used in every spot where that is accepted. Two macros are in place:
//! [`pezframe_support::dynamic_params::define_parameters` and
//! [`pezframe_support::dynamic_params:dynamic_pallet_params`] to define and expose parameters in a
//! typed manner.
//!
//! See the [`pezpallet`] module for more information about the interfaces this pezpallet exposes,
//! including its configuration trait, dispatchables, storage items, events and errors.
//!
//! ## Overview
//!
//! This pezpallet is a good fit for updating parameters without a runtime upgrade. It is very handy
//! to not require a runtime upgrade for a simple parameter change since runtime upgrades require a
//! lot of diligence and always bear risks. It seems overkill to update the whole runtime for a
//! simple parameter change. This pezpallet allows for fine-grained control over who can update
//! what. The only down-side is that it trades off performance with convenience and should therefore
//! only be used in places where that is proven to be uncritical. Values that are rarely accessed
//! but change often would be a perfect fit.
//!
//! ### Example Configuration
//!
//! Here is an example of how to define some parameters, including their default values:
#![doc = docify::embed!("src/tests/mock.rs", dynamic_params)]
//!
//! A permissioned origin can be define on a per-key basis like this:
#![doc = docify::embed!("src/tests/mock.rs", custom_origin)]
//!
//! The pezpallet will also require a default value for benchmarking. Ideally this is the variant
//! with the longest encoded length. Although in either case the PoV benchmarking will take the
//! worst case over the whole enum.
#![doc = docify::embed!("src/tests/mock.rs", benchmarking_default)]
//!
//! Now the aggregated parameter needs to be injected into the pezpallet config:
#![doc = docify::embed!("src/tests/mock.rs", impl_config)]
//!
//! As last step, the parameters can now be used in other pallets 🙌
#![doc = docify::embed!("src/tests/mock.rs", usage)]
//!
//! ### Examples Usage
//!
//! Now to demonstrate how the values can be updated:
#![doc = docify::embed!("src/tests/unit.rs", set_parameters_example)]
//!
//! ## Low Level / Implementation Details
//!
//! The pezpallet stores the parameters in a storage map and implements the matching `Get<Value>`
//! for each `Key` type. The `Get` then accesses the `Parameters` map to retrieve the value. An
//! event is emitted every time that a value was updated. It is even emitted when the value is
//! changed to the same.
//!
//! The key and value types themselves are defined by macros and aggregated into a runtime wide
//! enum. This enum is then injected into the pezpallet. This allows it to be used without any
//! changes to the pezpallet that the parameter will be utilized by.
//!
//! ### Design Goals
//!
//! 1. Easy to update without runtime upgrade.
//! 2. Exposes metadata and docs for user convenience.
//! 3. Can be permissioned on a per-key base.
//!
//! ### Design
//!
//! 1. Everything is done at runtime without the need for `const` values. `Get` allows for this -
//! which is coincidentally an upside and a downside. 2. The types are defined through macros, which
//! allows to expose metadata and docs. 3. Access control is done through the `EnsureOriginWithArg`
//! trait, that allows to pass data along to the origin check. It gets passed in the key. The
//! implementor can then match on the key and the origin to decide whether the origin is
//! permissioned to set the value.
use pezframe_support::pezpallet_prelude::*;
use pezframe_system::pezpallet_prelude::*;
use pezframe_support::traits::{
dynamic_params::{AggregatedKeyValue, IntoKey, Key, RuntimeParameterStore, TryIntoKey},
EnsureOriginWithArg,
};
mod benchmarking;
#[cfg(test)]
mod tests;
mod weights;
pub use pezpallet::*;
pub use weights::WeightInfo;
/// The key type of a parameter.
type KeyOf<T> = <<T as Config>::RuntimeParameters as AggregatedKeyValue>::Key;
/// The value type of a parameter.
type ValueOf<T> = <<T as Config>::RuntimeParameters as AggregatedKeyValue>::Value;
#[pezframe_support::pezpallet]
pub mod pezpallet {
use super::*;
#[pezpallet::config(with_default)]
pub trait Config: pezframe_system::Config {
/// The overarching event type.
#[pezpallet::no_default_bounds]
#[allow(deprecated)]
type RuntimeEvent: From<Event<Self>>
+ IsType<<Self as pezframe_system::Config>::RuntimeEvent>;
/// The overarching KV type of the parameters.
///
/// Usually created by [`pezframe_support::dynamic_params`] or equivalent.
#[pezpallet::no_default_bounds]
type RuntimeParameters: AggregatedKeyValue;
/// The origin which may update a parameter.
///
/// The key of the parameter is passed in as second argument to allow for fine grained
/// control.
#[pezpallet::no_default_bounds]
type AdminOrigin: EnsureOriginWithArg<Self::RuntimeOrigin, KeyOf<Self>>;
/// Weight information for extrinsics in this module.
type WeightInfo: WeightInfo;
}
#[pezpallet::event]
#[pezpallet::generate_deposit(pub(crate) fn deposit_event)]
pub enum Event<T: Config> {
/// A Parameter was set.
///
/// Is also emitted when the value was not changed.
Updated {
/// The key that was updated.
key: <T::RuntimeParameters as AggregatedKeyValue>::Key,
/// The old value before this call.
old_value: Option<<T::RuntimeParameters as AggregatedKeyValue>::Value>,
/// The new value after this call.
new_value: Option<<T::RuntimeParameters as AggregatedKeyValue>::Value>,
},
}
/// Stored parameters.
#[pezpallet::storage]
pub type Parameters<T: Config> =
StorageMap<_, Blake2_128Concat, KeyOf<T>, ValueOf<T>, OptionQuery>;
#[pezpallet::pezpallet]
pub struct Pezpallet<T>(_);
#[pezpallet::call]
impl<T: Config> Pezpallet<T> {
/// Set the value of a parameter.
///
/// The dispatch origin of this call must be `AdminOrigin` for the given `key`. Values be
/// deleted by setting them to `None`.
#[pezpallet::call_index(0)]
#[pezpallet::weight(T::WeightInfo::set_parameter())]
pub fn set_parameter(
origin: OriginFor<T>,
key_value: T::RuntimeParameters,
) -> DispatchResult {
let (key, new) = key_value.into_parts();
T::AdminOrigin::ensure_origin(origin, &key)?;
let mut old = None;
Parameters::<T>::mutate(&key, |v| {
old = v.clone();
*v = new.clone();
});
Self::deposit_event(Event::Updated { key, old_value: old, new_value: new });
Ok(())
}
}
/// Default implementations of [`DefaultConfig`], which can be used to implement [`Config`].
pub mod config_preludes {
use super::*;
use pezframe_support::derive_impl;
/// A configuration for testing.
pub struct TestDefaultConfig;
#[derive_impl(pezframe_system::config_preludes::TestDefaultConfig, no_aggregated_types)]
impl pezframe_system::DefaultConfig for TestDefaultConfig {}
#[pezframe_support::register_default_impl(TestDefaultConfig)]
impl DefaultConfig for TestDefaultConfig {
#[inject_runtime_type]
type RuntimeEvent = ();
#[inject_runtime_type]
type RuntimeParameters = ();
type AdminOrigin = pezframe_support::traits::AsEnsureOriginWithArg<
pezframe_system::EnsureRoot<Self::AccountId>,
>;
type WeightInfo = ();
}
}
}
impl<T: Config> RuntimeParameterStore for Pezpallet<T> {
type AggregatedKeyValue = T::RuntimeParameters;
fn get<KV, K>(key: K) -> Option<K::Value>
where
KV: AggregatedKeyValue,
K: Key + Into<<KV as AggregatedKeyValue>::Key>,
<KV as AggregatedKeyValue>::Key: IntoKey<
<<Self as RuntimeParameterStore>::AggregatedKeyValue as AggregatedKeyValue>::Key,
>,
<<Self as RuntimeParameterStore>::AggregatedKeyValue as AggregatedKeyValue>::Value:
TryIntoKey<<KV as AggregatedKeyValue>::Value>,
<KV as AggregatedKeyValue>::Value: TryInto<K::WrappedValue>,
{
let key: <KV as AggregatedKeyValue>::Key = key.into();
let val = Parameters::<T>::get(key.into_key());
val.and_then(|v| {
let val: <KV as AggregatedKeyValue>::Value = v.try_into_key().ok()?;
let val: K::WrappedValue = val.try_into().ok()?;
let val = val.into();
Some(val)
})
}
}