// 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 ⚠️** //! //!
//! THIS CRATE IS NOT AUDITED AND SHOULD NOT BE USED IN PRODUCTION. //!
//! //! # 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` //! 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 = <::RuntimeParameters as AggregatedKeyValue>::Key; /// The value type of a parameter. type ValueOf = <::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> + IsType<::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>; /// Weight information for extrinsics in this module. type WeightInfo: WeightInfo; } #[pezpallet::event] #[pezpallet::generate_deposit(pub(crate) fn deposit_event)] pub enum Event { /// A Parameter was set. /// /// Is also emitted when the value was not changed. Updated { /// The key that was updated. key: ::Key, /// The old value before this call. old_value: Option<::Value>, /// The new value after this call. new_value: Option<::Value>, }, } /// Stored parameters. #[pezpallet::storage] pub type Parameters = StorageMap<_, Blake2_128Concat, KeyOf, ValueOf, OptionQuery>; #[pezpallet::pezpallet] pub struct Pezpallet(_); #[pezpallet::call] impl Pezpallet { /// 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, key_value: T::RuntimeParameters, ) -> DispatchResult { let (key, new) = key_value.into_parts(); T::AdminOrigin::ensure_origin(origin, &key)?; let mut old = None; Parameters::::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, >; type WeightInfo = (); } } } impl RuntimeParameterStore for Pezpallet { type AggregatedKeyValue = T::RuntimeParameters; fn get(key: K) -> Option where KV: AggregatedKeyValue, K: Key + Into<::Key>, ::Key: IntoKey< <::AggregatedKeyValue as AggregatedKeyValue>::Key, >, <::AggregatedKeyValue as AggregatedKeyValue>::Value: TryIntoKey<::Value>, ::Value: TryInto, { let key: ::Key = key.into(); let val = Parameters::::get(key.into_key()); val.and_then(|v| { let val: ::Value = v.try_into_key().ok()?; let val: K::WrappedValue = val.try_into().ok()?; let val = val.into(); Some(val) }) } }