[FRAME] Parameters pallet (#2061)

Closes #169  

Fork of the `orml-parameters-pallet` as introduced by
https://github.com/open-web3-stack/open-runtime-module-library/pull/927
(cc @xlc)
It greatly changes how the macros work, but keeps the pallet the same.
The downside of my code is now that it does only support constant keys
in the form of types, not value-bearing keys.
I think this is an acceptable trade off, give that it can be used by
*any* pallet without any changes.

The pallet allows to dynamically set parameters that can be used in
pallet configs while also restricting the updating on a per-key basis.
The rust-docs contains a complete example.

Changes:
- Add `parameters-pallet`
- Use in the kitchensink as demonstration
- Add experimental attribute to define dynamic params in the runtime.
- Adding a bunch of traits to `frame_support::traits::dynamic_params`
that can be re-used by the ORML macros

## Example

First to define the parameters in the runtime file. The syntax is very
explicit about the codec index and errors if there is no.
```rust
#[dynamic_params(RuntimeParameters, pallet_parameters::Parameters::<Runtime>))]
pub mod dynamic_params {
	use super::*;

	#[dynamic_pallet_params]
	#[codec(index = 0)]
	pub mod storage {
		/// Configures the base deposit of storing some data.
		#[codec(index = 0)]
		pub static BaseDeposit: Balance = 1 * DOLLARS;

		/// Configures the per-byte deposit of storing some data.
		#[codec(index = 1)]
		pub static ByteDeposit: Balance = 1 * CENTS;
	}

	#[dynamic_pallet_params]
	#[codec(index = 1)]
	pub mod contracts {
		#[codec(index = 0)]
		pub static DepositPerItem: Balance = deposit(1, 0);

		#[codec(index = 1)]
		pub static DepositPerByte: Balance = deposit(0, 1);
	}
}
```

Then the pallet is configured with the aggregate:  
```rust
impl pallet_parameters::Config for Runtime {
	type AggregratedKeyValue = RuntimeParameters;
	type AdminOrigin = EnsureRootWithSuccess<AccountId, ConstBool<true>>;
	...
}
```

And then the parameters can be used in a pallet config:
```rust
impl pallet_preimage::Config for Runtime {
	type DepositBase = dynamic_params::storage::DepositBase;
}
```

A custom origin an be defined like this:  
```rust
pub struct DynamicParametersManagerOrigin;

impl EnsureOriginWithArg<RuntimeOrigin, RuntimeParametersKey> for DynamicParametersManagerOrigin {
	type Success = ();

	fn try_origin(
		origin: RuntimeOrigin,
		key: &RuntimeParametersKey,
	) -> Result<Self::Success, RuntimeOrigin> {
		match key {
			RuntimeParametersKey::Storage(_) => {
				frame_system::ensure_root(origin.clone()).map_err(|_| origin)?;
				return Ok(())
			},
			RuntimeParametersKey::Contract(_) => {
				frame_system::ensure_root(origin.clone()).map_err(|_| origin)?;
				return Ok(())
			},
		}
	}

	#[cfg(feature = "runtime-benchmarks")]
	fn try_successful_origin(_key: &RuntimeParametersKey) -> Result<RuntimeOrigin, ()> {
		Ok(RuntimeOrigin::Root)
	}
}
```

---------

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Co-authored-by: Nikhil Gupta <17176722+gupnik@users.noreply.github.com>
Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>
Co-authored-by: command-bot <>
This commit is contained in:
Oliver Tale-Yazdi
2024-02-08 19:47:04 +01:00
committed by GitHub
parent aac07af03c
commit e53ebd8cd4
21 changed files with 2017 additions and 18 deletions
+9 -1
View File
@@ -44,7 +44,7 @@ pub mod __private {
pub use paste;
pub use scale_info;
pub use serde;
pub use sp_core::{OpaqueMetadata, Void};
pub use sp_core::{Get, OpaqueMetadata, Void};
pub use sp_crypto_hashing_proc_macro;
pub use sp_inherents;
#[cfg(feature = "std")]
@@ -175,6 +175,14 @@ pub use frame_support_procedural::storage_alias;
pub use frame_support_procedural::derive_impl;
/// Experimental macros for defining dynamic params that can be used in pallet configs.
#[cfg(feature = "experimental")]
pub mod dynamic_params {
pub use frame_support_procedural::{
dynamic_aggregated_params_internal, dynamic_pallet_params, dynamic_params,
};
}
/// Create new implementations of the [`Get`](crate::traits::Get) trait.
///
/// The so-called parameter type can be created in four different ways:
+2
View File
@@ -124,6 +124,8 @@ pub use safe_mode::{SafeMode, SafeModeError, SafeModeNotify};
mod tx_pause;
pub use tx_pause::{TransactionPause, TransactionPauseError};
pub mod dynamic_params;
pub mod tasks;
pub use tasks::Task;
@@ -0,0 +1,153 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// 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.
//! Types and traits for dynamic parameters.
//!
//! Can be used by 3rd party macros to define dynamic parameters that are compatible with the the
//! `parameters` pallet.
use codec::MaxEncodedLen;
use frame_support::Parameter;
/// A dynamic parameter store across an aggregated KV type.
pub trait RuntimeParameterStore {
type AggregratedKeyValue: AggregratedKeyValue;
/// Get the value of a parametrized key.
///
/// Should return `None` if no explicit value was set instead of a default.
fn get<KV, K>(key: K) -> Option<K::Value>
where
KV: AggregratedKeyValue,
K: Key + Into<<KV as AggregratedKeyValue>::Key>,
<KV as AggregratedKeyValue>::Key: IntoKey<
<<Self as RuntimeParameterStore>::AggregratedKeyValue as AggregratedKeyValue>::Key,
>,
<<Self as RuntimeParameterStore>::AggregratedKeyValue as AggregratedKeyValue>::Value:
TryIntoKey<<KV as AggregratedKeyValue>::Value>,
<KV as AggregratedKeyValue>::Value: TryInto<K::WrappedValue>;
}
/// A dynamic parameter store across a concrete KV type.
pub trait ParameterStore<KV: AggregratedKeyValue> {
/// Get the value of a parametrized key.
fn get<K>(key: K) -> Option<K::Value>
where
K: Key + Into<<KV as AggregratedKeyValue>::Key>,
<KV as AggregratedKeyValue>::Value: TryInto<K::WrappedValue>;
}
/// Key of a dynamic parameter.
pub trait Key {
/// The value that the key is parametrized with.
type Value;
/// An opaque representation of `Self::Value`.
type WrappedValue: Into<Self::Value>;
}
/// The aggregated key-value type of a dynamic parameter store.
pub trait AggregratedKeyValue: Parameter {
/// The aggregated key type.
type Key: Parameter + MaxEncodedLen;
/// The aggregated value type.
type Value: Parameter + MaxEncodedLen;
/// Split the aggregated key-value type into its parts.
fn into_parts(self) -> (Self::Key, Option<Self::Value>);
}
impl AggregratedKeyValue for () {
type Key = ();
type Value = ();
fn into_parts(self) -> (Self::Key, Option<Self::Value>) {
((), None)
}
}
/// Allows to create a `ParameterStore` from a `RuntimeParameterStore`.
///
/// This concretization is useful when configuring pallets, since a pallet will require a parameter
/// store for its own KV type and not the aggregated runtime-wide KV type.
pub struct ParameterStoreAdapter<PS, KV>(sp_std::marker::PhantomData<(PS, KV)>);
impl<PS, KV> ParameterStore<KV> for ParameterStoreAdapter<PS, KV>
where
PS: RuntimeParameterStore,
KV: AggregratedKeyValue,
<KV as AggregratedKeyValue>::Key:
IntoKey<<<PS as RuntimeParameterStore>::AggregratedKeyValue as AggregratedKeyValue>::Key>,
<KV as AggregratedKeyValue>::Value: TryFromKey<
<<PS as RuntimeParameterStore>::AggregratedKeyValue as AggregratedKeyValue>::Value,
>,
{
fn get<K>(key: K) -> Option<K::Value>
where
K: Key + Into<<KV as AggregratedKeyValue>::Key>,
<KV as AggregratedKeyValue>::Value: TryInto<K::WrappedValue>,
{
PS::get::<KV, K>(key)
}
}
// workaround for rust bug https://github.com/rust-lang/rust/issues/51445
mod workaround {
pub trait FromKey<T>: Sized {
#[must_use]
fn from_key(value: T) -> Self;
}
pub trait IntoKey<T>: Sized {
#[must_use]
fn into_key(self) -> T;
}
impl<T, U> IntoKey<U> for T
where
U: FromKey<T>,
{
fn into_key(self) -> U {
U::from_key(self)
}
}
pub trait TryIntoKey<T>: Sized {
type Error;
fn try_into_key(self) -> Result<T, Self::Error>;
}
pub trait TryFromKey<T>: Sized {
type Error;
fn try_from_key(value: T) -> Result<Self, Self::Error>;
}
impl<T, U> TryIntoKey<U> for T
where
U: TryFromKey<T>,
{
type Error = U::Error;
fn try_into_key(self) -> Result<U, U::Error> {
U::try_from_key(self)
}
}
}
pub use workaround::*;