Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Commit

Permalink
Adds support for storage parameter types (#6296)
Browse files Browse the repository at this point in the history
* Adds support for storage parameter types

This pr adds a new parameter types type, the storage parameter types.
This parameter type supports loading the value from the storage or
returning the given default value.

* Use twox_128

* Update docs

* Update frame/support/src/lib.rs

Co-authored-by: Alexander Popiak <alexander.popiak@parity.io>

Co-authored-by: Alexander Popiak <alexander.popiak@parity.io>
  • Loading branch information
bkchr and apopiak authored Jun 11, 2020
1 parent 4bd0785 commit 75113aa
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 20 deletions.
119 changes: 101 additions & 18 deletions frame/support/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ pub use paste;
#[doc(hidden)]
pub use sp_state_machine::BasicExternalities;
#[doc(hidden)]
pub use sp_io::storage::root as storage_root;
pub use sp_io::{storage::root as storage_root, self};
#[doc(hidden)]
pub use sp_runtime::RuntimeDebug;

Expand Down Expand Up @@ -84,8 +84,24 @@ pub use sp_runtime::{self, ConsensusEngineId, print, traits::Printable};
#[derive(Debug)]
pub enum Never {}

/// Macro for easily creating a new implementation of the `Get` trait. If `const` token is used, the
/// rhs of the expression must be `const`-only, and get is implemented as `const`:
/// Create new implementations of the [`Get`](crate::traits::Get) trait.
///
/// The so-called parameter type can be created in three different ways:
///
/// - Using `const` to create a parameter type that provides a `const` getter.
/// It is required that the `value` is const.
///
/// - Declare the parameter type without `const` to have more freedom when creating the value.
///
/// - Using `storage` to create a storage parameter type. This type is special as it tries to
/// load the value from the storage under a fixed key. If the value could not be found in the
/// storage, the given default value will be returned. It is required that the value implements
/// [`Encode`](codec::Encode) and [`Decode`](codec::Decode). The key for looking up the value
/// in the storage is built using the following formular:
///
/// `twox_128(":" ++ NAME ++ ":")` where `NAME` is the name that is passed as type name.
///
/// # Examples
///
/// ```
/// # use frame_support::traits::Get;
Expand All @@ -95,23 +111,27 @@ pub enum Never {}
///
/// const FIXED_VALUE: u64 = 10;
/// parameter_types! {
/// pub const Argument: u64 = 42 + FIXED_VALUE;
/// pub OtherArgument: u64 = non_const_expression();
/// pub const Argument: u64 = 42 + FIXED_VALUE;
/// /// Visibility of the type is optional
/// OtherArgument: u64 = non_const_expression();
/// pub storage StorageArgument: u64 = 5;
/// }
///
/// trait Config {
/// type Parameter: Get<u64>;
/// type OtherParameter: Get<u64>;
/// type Parameter: Get<u64>;
/// type OtherParameter: Get<u64>;
/// type StorageParameter: Get<u64>;
/// }
///
/// struct Runtime;
/// impl Config for Runtime {
/// type Parameter = Argument;
/// type OtherParameter = OtherArgument;
/// type Parameter = Argument;
/// type OtherParameter = OtherArgument;
/// type StorageParameter = StorageArgument;
/// }
/// ```
///
/// Invalid example:
/// # Invalid example:
///
/// ```compile_fail
/// # use frame_support::traits::Get;
Expand All @@ -120,7 +140,7 @@ pub enum Never {}
/// fn non_const_expression() -> u64 { 99 }
///
/// parameter_types! {
/// pub const Argument: u64 = non_const_expression();
/// pub const Argument: u64 = non_const_expression();
/// }
/// ```

Expand All @@ -133,8 +153,8 @@ macro_rules! parameter_types {
) => (
$( #[ $attr ] )*
$vis struct $name;
$crate::parameter_types!{IMPL_CONST $name , $type , $value}
$crate::parameter_types!{ $( $rest )* }
$crate::parameter_types!(IMPL_CONST $name , $type , $value);
$crate::parameter_types!( $( $rest )* );
);
(
$( #[ $attr:meta ] )*
Expand All @@ -143,33 +163,79 @@ macro_rules! parameter_types {
) => (
$( #[ $attr ] )*
$vis struct $name;
$crate::parameter_types!{IMPL $name , $type , $value}
$crate::parameter_types!{ $( $rest )* }
$crate::parameter_types!(IMPL $name, $type, $value);
$crate::parameter_types!( $( $rest )* );
);
(
$( #[ $attr:meta ] )*
$vis:vis storage $name:ident: $type:ty = $value:expr;
$( $rest:tt )*
) => (
$( #[ $attr ] )*
$vis struct $name;
$crate::parameter_types!(IMPL_STORAGE $name, $type, $value);
$crate::parameter_types!( $( $rest )* );
);
() => ();
(IMPL_CONST $name:ident , $type:ty , $value:expr) => {
(IMPL_CONST $name:ident, $type:ty, $value:expr) => {
impl $name {
/// Returns the value of this parameter type.
pub const fn get() -> $type {
$value
}
}

impl<I: From<$type>> $crate::traits::Get<I> for $name {
fn get() -> I {
I::from($value)
}
}
};
(IMPL $name:ident , $type:ty , $value:expr) => {
(IMPL $name:ident, $type:ty, $value:expr) => {
impl $name {
/// Returns the value of this parameter type.
pub fn get() -> $type {
$value
}
}

impl<I: From<$type>> $crate::traits::Get<I> for $name {
fn get() -> I {
I::from($value)
}
}
};
(IMPL_STORAGE $name:ident, $type:ty, $value:expr) => {
impl $name {
/// Returns the key for this parameter type.
pub fn key() -> [u8; 16] {
$crate::sp_io::hashing::twox_128(
concat!(":", stringify!($name), ":").as_bytes()
)
}

/// Set the value of this parameter type in the storage.
///
/// This needs to be executed in an externalities provided
/// environment.
pub fn set(value: &$type) {
$crate::storage::unhashed::put(&Self::key(), value);
}

/// Returns the value of this parameter type.
///
/// This needs to be executed in an externalities provided
/// environment.
pub fn get() -> $type {
$crate::storage::unhashed::get(&Self::key()).unwrap_or_else(|| $value)
}
}

impl<I: From<$type>> $crate::traits::Get<I> for $name {
fn get() -> I {
I::from(Self::get())
}
}
}
}

Expand Down Expand Up @@ -316,6 +382,7 @@ mod tests {
StorageEntryModifier, DefaultByteGetter, StorageHasher,
};
use sp_std::marker::PhantomData;
use sp_io::TestExternalities;

pub trait Trait {
type BlockNumber: Codec + EncodeLike + Default;
Expand Down Expand Up @@ -361,7 +428,7 @@ mod tests {
type Origin = u32;
}

fn new_test_ext() -> sp_io::TestExternalities {
fn new_test_ext() -> TestExternalities {
GenesisConfig::default().build_storage().unwrap().into()
}

Expand Down Expand Up @@ -696,4 +763,20 @@ mod tests {
let metadata = Module::<Test>::storage_metadata();
pretty_assertions::assert_eq!(EXPECTED_METADATA, metadata);
}

parameter_types! {
storage StorageParameter: u64 = 10;
}

#[test]
fn check_storage_parameter_type_works() {
TestExternalities::default().execute_with(|| {
assert_eq!(sp_io::hashing::twox_128(b":StorageParameter:"), StorageParameter::key());

assert_eq!(10, StorageParameter::get());

StorageParameter::set(&300);
assert_eq!(300, StorageParameter::get());
})
}
}
6 changes: 4 additions & 2 deletions frame/support/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -450,9 +450,11 @@ impl<T: IntoIterator + Clone,> Len for T where <T as IntoIterator>::IntoIter: Ex
}
}

/// A trait for querying a single fixed value from a type.
/// A trait for querying a single value from a type.
///
/// It is not required that the value is constant.
pub trait Get<T> {
/// Return a constant value.
/// Return the current value.
fn get() -> T;
}

Expand Down

0 comments on commit 75113aa

Please sign in to comment.