diff --git a/substrate/frame/support/src/dispatch.rs b/substrate/frame/support/src/dispatch.rs index 4287f2c1ceba39cc16c663845859fb182b39514d..31e3efb001a70824cd31f884de238d2b8914b773 100644 --- a/substrate/frame/support/src/dispatch.rs +++ b/substrate/frame/support/src/dispatch.rs @@ -46,10 +46,6 @@ pub type DispatchResult = Result<(), sp_runtime::DispatchError>; pub type DispatchErrorWithPostInfo = sp_runtime::DispatchErrorWithPostInfo<crate::weights::PostDispatchInfo>; - -/// A type that cannot be instantiated. -pub enum Never {} - /// Serializable version of Dispatchable. /// This value can be used as a "function" in an extrinsic. pub trait Callable<T> { @@ -1316,7 +1312,7 @@ macro_rules! decl_module { { #[doc(hidden)] #[codec(skip)] - __PhantomItem($crate::sp_std::marker::PhantomData<($trait_instance, $($instance)?)>, $crate::dispatch::Never), + __PhantomItem($crate::sp_std::marker::PhantomData<($trait_instance, $($instance)?)>, $crate::Never), $( $generated_variants )* } }; diff --git a/substrate/frame/support/src/error.rs b/substrate/frame/support/src/error.rs index a06f46889224dd7fb1ebcbe56426ce35e1ecb895..2fdba88156404a31ff7801f2d9532209a40a96a9 100644 --- a/substrate/frame/support/src/error.rs +++ b/substrate/frame/support/src/error.rs @@ -89,7 +89,7 @@ macro_rules! decl_error { #[doc(hidden)] __Ignore( $crate::sp_std::marker::PhantomData<($generic, $( $inst_generic)?)>, - $crate::dispatch::Never, + $crate::Never, ), $( $( #[doc = $doc_attr] )* diff --git a/substrate/frame/support/src/lib.rs b/substrate/frame/support/src/lib.rs index 8a88496eda0bab28a1eec585cdd971000446f60d..49ad802d07dc48624ac4c12e3b2e0a72731017cb 100644 --- a/substrate/frame/support/src/lib.rs +++ b/substrate/frame/support/src/lib.rs @@ -78,6 +78,10 @@ pub use self::storage::{ pub use self::dispatch::{Parameter, Callable, IsSubType}; pub use sp_runtime::{self, ConsensusEngineId, print, traits::Printable}; +/// A type that cannot be instantiated. +#[derive(Debug)] +pub enum Never {} + /// Macro for easily creating a new implementation of the `Get` trait. Use similarly to /// how you would declare a `const`: /// diff --git a/substrate/frame/support/src/storage/generator/double_map.rs b/substrate/frame/support/src/storage/generator/double_map.rs index e23b332383c68b36b4b05c7950d1bd6d9473fdb9..c7e4c10017e706275727f687228516bb6e55aca0 100644 --- a/substrate/frame/support/src/storage/generator/double_map.rs +++ b/substrate/frame/support/src/storage/generator/double_map.rs @@ -17,7 +17,7 @@ use sp_std::prelude::*; use sp_std::borrow::Borrow; use codec::{Ref, FullCodec, FullEncode, Decode, Encode, EncodeLike, EncodeAppend}; -use crate::{storage::{self, unhashed}, traits::Len}; +use crate::{storage::{self, unhashed}, traits::Len, Never}; use crate::hash::{StorageHasher, Twox128, ReversibleStorageHasher}; /// Generator for `StorageDoubleMap` used by `decl_storage`. @@ -223,14 +223,24 @@ impl<K1, K2, V, G> storage::StorageDoubleMap<K1, K2, V> for G where KArg1: EncodeLike<K1>, KArg2: EncodeLike<K2>, F: FnOnce(&mut Self::Query) -> R, + { + Self::try_mutate(k1, k2, |v| Ok::<R, Never>(f(v))).expect("`Never` can not be constructed; qed") + } + + fn try_mutate<KArg1, KArg2, R, E, F>(k1: KArg1, k2: KArg2, f: F) -> Result<R, E> where + KArg1: EncodeLike<K1>, + KArg2: EncodeLike<K2>, + F: FnOnce(&mut Self::Query) -> Result<R, E>, { let final_key = Self::storage_double_map_final_key(k1, k2); let mut val = G::from_optional_value_to_query(unhashed::get(final_key.as_ref())); let ret = f(&mut val); - match G::from_query_to_optional_value(val) { - Some(ref val) => unhashed::put(final_key.as_ref(), val), - None => unhashed::kill(final_key.as_ref()), + if ret.is_ok() { + match G::from_query_to_optional_value(val) { + Some(ref val) => unhashed::put(final_key.as_ref(), val), + None => unhashed::kill(final_key.as_ref()), + } } ret } diff --git a/substrate/frame/support/src/storage/generator/map.rs b/substrate/frame/support/src/storage/generator/map.rs index c29a9a223aacf578d162864ef9a119aa015e78bc..cc871072f5f40f550464c33400978b53358970a8 100644 --- a/substrate/frame/support/src/storage/generator/map.rs +++ b/substrate/frame/support/src/storage/generator/map.rs @@ -18,7 +18,7 @@ use sp_std::prelude::*; use sp_std::borrow::Borrow; use codec::{FullCodec, FullEncode, Decode, Encode, EncodeLike, Ref, EncodeAppend}; -use crate::{storage::{self, unhashed}, traits::Len}; +use crate::{storage::{self, unhashed}, traits::Len, Never}; use crate::hash::{StorageHasher, Twox128, ReversibleStorageHasher}; /// Generator for `StorageMap` used by `decl_storage`. @@ -229,27 +229,11 @@ impl<K: FullEncode, V: FullCodec, G: StorageMap<K, V>> storage::StorageMap<K, V> } fn mutate<KeyArg: EncodeLike<K>, R, F: FnOnce(&mut Self::Query) -> R>(key: KeyArg, f: F) -> R { - let final_key = Self::storage_map_final_key(key); - let mut val = G::from_optional_value_to_query(unhashed::get(final_key.as_ref())); - - let ret = f(&mut val); - match G::from_query_to_optional_value(val) { - Some(ref val) => unhashed::put(final_key.as_ref(), &val), - None => unhashed::kill(final_key.as_ref()), - } - ret + Self::try_mutate(key, |v| Ok::<R, Never>(f(v))).expect("`Never` can not be constructed; qed") } fn mutate_exists<KeyArg: EncodeLike<K>, R, F: FnOnce(&mut Option<V>) -> R>(key: KeyArg, f: F) -> R { - let final_key = Self::storage_map_final_key(key); - let mut val = unhashed::get(final_key.as_ref()); - - let ret = f(&mut val); - match val { - Some(ref val) => unhashed::put(final_key.as_ref(), &val), - None => unhashed::kill(final_key.as_ref()), - } - ret + Self::try_mutate_exists(key, |v| Ok::<R, Never>(f(v))).expect("`Never` can not be constructed; qed") } fn try_mutate<KeyArg: EncodeLike<K>, R, E, F: FnOnce(&mut Self::Query) -> Result<R, E>>( diff --git a/substrate/frame/support/src/storage/generator/mod.rs b/substrate/frame/support/src/storage/generator/mod.rs index 687d8a3c9361ba1ca217ee22947a349ae9095955..07e75055f356a87435fc0ec700e5afdb0795c877 100644 --- a/substrate/frame/support/src/storage/generator/mod.rs +++ b/substrate/frame/support/src/storage/generator/mod.rs @@ -37,6 +37,7 @@ mod tests { use sp_io::TestExternalities; use codec::Encode; use crate::storage::{unhashed, generator::StorageValue, IterableStorageMap}; + use crate::{assert_noop, assert_ok}; struct Runtime {} pub trait Trait { @@ -57,6 +58,7 @@ mod tests { trait Store for Module<T: Trait> as Runtime { Value get(fn value) config(): (u64, u64); NumberMap: map hasher(identity) u32 => u64; + DoubleMap: double_map hasher(identity) u32, hasher(identity) u32 => u64; } } @@ -102,4 +104,54 @@ mod tests { ); }) } + + #[test] + fn try_mutate_works() { + let t = GenesisConfig::default().build_storage().unwrap(); + TestExternalities::new(t).execute_with(|| { + assert_eq!(Value::get(), (0, 0)); + assert_eq!(NumberMap::get(0), 0); + assert_eq!(DoubleMap::get(0, 0), 0); + + // `assert_noop` ensures that the state does not change + assert_noop!(Value::try_mutate(|value| -> Result<(), &'static str> { + *value = (2, 2); + Err("don't change value") + }), "don't change value"); + + assert_noop!(NumberMap::try_mutate(0, |value| -> Result<(), &'static str> { + *value = 4; + Err("don't change value") + }), "don't change value"); + + assert_noop!(DoubleMap::try_mutate(0, 0, |value| -> Result<(), &'static str> { + *value = 6; + Err("don't change value") + }), "don't change value"); + + // Showing this explicitly for clarity + assert_eq!(Value::get(), (0, 0)); + assert_eq!(NumberMap::get(0), 0); + assert_eq!(DoubleMap::get(0, 0), 0); + + assert_ok!(Value::try_mutate(|value| -> Result<(), &'static str> { + *value = (2, 2); + Ok(()) + })); + + assert_ok!(NumberMap::try_mutate(0, |value| -> Result<(), &'static str> { + *value = 4; + Ok(()) + })); + + assert_ok!(DoubleMap::try_mutate(0, 0, |value| -> Result<(), &'static str> { + *value = 6; + Ok(()) + })); + + assert_eq!(Value::get(), (2, 2)); + assert_eq!(NumberMap::get(0), 4); + assert_eq!(DoubleMap::get(0, 0), 6); + }); + } } diff --git a/substrate/frame/support/src/storage/generator/value.rs b/substrate/frame/support/src/storage/generator/value.rs index 9e26131f48949637be4a8779169e97bddc9396a0..dd9bbded4b8ebb10f8553a24db37099935a15290 100644 --- a/substrate/frame/support/src/storage/generator/value.rs +++ b/substrate/frame/support/src/storage/generator/value.rs @@ -17,7 +17,12 @@ #[cfg(not(feature = "std"))] use sp_std::prelude::*; use codec::{FullCodec, Encode, EncodeAppend, EncodeLike, Decode}; -use crate::{storage::{self, unhashed}, hash::{Twox128, StorageHasher}, traits::Len}; +use crate::{ + Never, + storage::{self, unhashed}, + hash::{Twox128, StorageHasher}, + traits::Len +}; /// Generator for `StorageValue` used by `decl_storage`. /// @@ -104,12 +109,18 @@ impl<T: FullCodec, G: StorageValue<T>> storage::StorageValue<T> for G { } fn mutate<R, F: FnOnce(&mut G::Query) -> R>(f: F) -> R { + Self::try_mutate(|v| Ok::<R, Never>(f(v))).expect("`Never` can not be constructed; qed") + } + + fn try_mutate<R, E, F: FnOnce(&mut G::Query) -> Result<R, E>>(f: F) -> Result<R, E> { let mut val = G::get(); let ret = f(&mut val); - match G::from_query_to_optional_value(val) { - Some(ref val) => G::put(val), - None => G::kill(), + if ret.is_ok() { + match G::from_query_to_optional_value(val) { + Some(ref val) => G::put(val), + None => G::kill(), + } } ret } diff --git a/substrate/frame/support/src/storage/mod.rs b/substrate/frame/support/src/storage/mod.rs index 47201e22e602e9a8510de35ccf2de8eaa0181633..3a811c20e364433f9ea6bd542b95a9acddef2b01 100644 --- a/substrate/frame/support/src/storage/mod.rs +++ b/substrate/frame/support/src/storage/mod.rs @@ -80,6 +80,9 @@ pub trait StorageValue<T: FullCodec> { /// Mutate the value fn mutate<R, F: FnOnce(&mut Self::Query) -> R>(f: F) -> R; + /// Mutate the value if closure returns `Ok` + fn try_mutate<R, E, F: FnOnce(&mut Self::Query) -> Result<R, E>>(f: F) -> Result<R, E>; + /// Clear the storage value. fn kill(); @@ -280,21 +283,25 @@ pub trait StorageDoubleMap<K1: FullEncode, K2: FullEncode, V: FullCodec> { /// The type that get/take returns. type Query; + /// Get the storage key used to fetch a value corresponding to a specific key. fn hashed_key_for<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> Vec<u8> where KArg1: EncodeLike<K1>, KArg2: EncodeLike<K2>; + /// Does the value (explicitly) exist in storage? fn contains_key<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> bool where KArg1: EncodeLike<K1>, KArg2: EncodeLike<K2>; + /// Load the value associated with the given key from the double map. fn get<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> Self::Query where KArg1: EncodeLike<K1>, KArg2: EncodeLike<K2>; + /// Take a value from storage, removing it afterwards. fn take<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> Self::Query where KArg1: EncodeLike<K1>, @@ -308,28 +315,43 @@ pub trait StorageDoubleMap<K1: FullEncode, K2: FullEncode, V: FullCodec> { YKArg1: EncodeLike<K1>, YKArg2: EncodeLike<K2>; + /// Store a value to be associated with the given keys from the double map. fn insert<KArg1, KArg2, VArg>(k1: KArg1, k2: KArg2, val: VArg) where KArg1: EncodeLike<K1>, KArg2: EncodeLike<K2>, VArg: EncodeLike<V>; + /// Remove the value under the given keys. fn remove<KArg1, KArg2>(k1: KArg1, k2: KArg2) where KArg1: EncodeLike<K1>, KArg2: EncodeLike<K2>; + /// Remove all values under the first key. fn remove_prefix<KArg1>(k1: KArg1) where KArg1: ?Sized + EncodeLike<K1>; + /// Iterate over values that share the first key. fn iter_prefix_values<KArg1>(k1: KArg1) -> PrefixIterator<V> where KArg1: ?Sized + EncodeLike<K1>; + /// Mutate the value under the given keys. fn mutate<KArg1, KArg2, R, F>(k1: KArg1, k2: KArg2, f: F) -> R where KArg1: EncodeLike<K1>, KArg2: EncodeLike<K2>, F: FnOnce(&mut Self::Query) -> R; + /// Mutate the value under the given keys when the closure returns `Ok`. + fn try_mutate<KArg1, KArg2, R, E, F>(k1: KArg1, k2: KArg2, f: F) -> Result<R, E> + where + KArg1: EncodeLike<K1>, + KArg2: EncodeLike<K2>, + F: FnOnce(&mut Self::Query) -> Result<R, E>; + + /// Append the given item to the value in the storage. + /// + /// `V` is required to implement `codec::EncodeAppend`. fn append<Items, Item, EncodeLikeItem, KArg1, KArg2>( k1: KArg1, k2: KArg2, @@ -344,6 +366,10 @@ pub trait StorageDoubleMap<K1: FullEncode, K2: FullEncode, V: FullCodec> { Items: IntoIterator<Item=EncodeLikeItem>, Items::IntoIter: ExactSizeIterator; + /// Safely append the given items to the value in the storage. If a codec error occurs, then the + /// old (presumably corrupt) value is replaced with the given `items`. + /// + /// `V` is required to implement `codec::EncodeAppend`. fn append_or_insert<Items, Item, EncodeLikeItem, KArg1, KArg2>( k1: KArg1, k2: KArg2,