diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index 471dd72a748df..316e356759a17 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -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; @@ -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; @@ -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; -/// type OtherParameter: Get; +/// type Parameter: Get; +/// type OtherParameter: Get; +/// type StorageParameter: Get; /// } /// /// 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; @@ -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(); /// } /// ``` @@ -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 ] )* @@ -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> $crate::traits::Get 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> $crate::traits::Get 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> $crate::traits::Get for $name { + fn get() -> I { + I::from(Self::get()) + } + } } } @@ -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; @@ -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() } @@ -696,4 +763,20 @@ mod tests { let metadata = Module::::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()); + }) + } } diff --git a/frame/support/src/traits.rs b/frame/support/src/traits.rs index df47def8702bb..fae0ad2fcbc71 100644 --- a/frame/support/src/traits.rs +++ b/frame/support/src/traits.rs @@ -450,9 +450,11 @@ impl Len for T where ::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 { - /// Return a constant value. + /// Return the current value. fn get() -> T; }