diff --git a/substrate/node/runtime/src/lib.rs b/substrate/node/runtime/src/lib.rs index e80d5fa83ab381278c83756b2e4a45b7781694d4..01dff2b7ae8e6be012baa0df7a6fb6d1ade19f8c 100644 --- a/substrate/node/runtime/src/lib.rs +++ b/substrate/node/runtime/src/lib.rs @@ -81,7 +81,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // implementation changes and behavior does not, then leave spec_version as // is and increment impl_version. spec_version: 149, - impl_version: 149, + impl_version: 150, apis: RUNTIME_API_VERSIONS, }; diff --git a/substrate/srml/support/procedural/src/storage/impls.rs b/substrate/srml/support/procedural/src/storage/impls.rs index dd936636735141231ee6352e14c4032a00ad35df..d5f73f5d39fa67936348a8f5277841104e61c7d3 100644 --- a/substrate/srml/support/procedural/src/storage/impls.rs +++ b/substrate/srml/support/procedural/src/storage/impls.rs @@ -666,6 +666,42 @@ impl<'a, I: Iterator<Item=syn::Meta>> Impls<'a, I> { #mutate_impl; ret } + + // Swap must be overriden not to break links. + fn swap<S: #scrate::HashedStorage<Self::Hasher>>( + key1: &#kty, + key2: &#kty, + storage: &mut S, + ) { + use self::#inner_module::Utils; + + let final_key1 = &*#as_map::key_for(key1); + let final_key2 = &*#as_map::key_for(key2); + let full_value_1 = Self::read_with_linkage(storage, final_key1); + let full_value_2 = Self::read_with_linkage(storage, final_key2); + + match (full_value_1, full_value_2) { + // Just keep linkage in order and only swap values. + (Some((value1, linkage1)), Some((value2, linkage2))) => { + storage.put(final_key1, &(value2, linkage1)); + storage.put(final_key2, &(value1, linkage2)); + } + // Remove key and insert the new one. + (Some((value, linkage)), None) => { + #as_map::remove(key1, storage); + let linkage = Self::new_head_linkage(storage, key2); + storage.put(final_key2, &(value, linkage)); + } + // Remove key and insert the new one. + (None, Some((value, linkage))) => { + #as_map::remove(key2, storage); + let linkage = Self::new_head_linkage(storage, key1); + storage.put(final_key1, &(value, linkage)); + } + // No-op. + (None, None) => (), + } + } } impl<#impl_trait> #scrate::storage::hashed::generator::EnumerableStorageMap<#kty, #typ> diff --git a/substrate/srml/support/src/lib.rs b/substrate/srml/support/src/lib.rs index 5623cab70f2ab4dc9b1f3451d1e17f7a72e12554..16e3ca5de9a347fc6b44399884263aa7c00b0ee8 100644 --- a/substrate/srml/support/src/lib.rs +++ b/substrate/srml/support/src/lib.rs @@ -342,6 +342,35 @@ mod tests { }); } + #[test] + fn linked_map_swap_works() { + with_externalities(&mut new_test_ext(), || { + OptionLinkedMap::insert(0, 0); + OptionLinkedMap::insert(1, 1); + OptionLinkedMap::insert(2, 2); + OptionLinkedMap::insert(3, 3); + + let collect = || OptionLinkedMap::enumerate().collect::<Vec<_>>(); + assert_eq!(collect(), vec![(3, 3), (2, 2), (1, 1), (0, 0)]); + + // Two existing + OptionLinkedMap::swap(1, 2); + assert_eq!(collect(), vec![(3, 3), (2, 1), (1, 2), (0, 0)]); + + // Back to normal + OptionLinkedMap::swap(2, 1); + assert_eq!(collect(), vec![(3, 3), (2, 2), (1, 1), (0, 0)]); + + // Left existing + OptionLinkedMap::swap(2, 5); + assert_eq!(collect(), vec![(5, 2), (3, 3), (1, 1), (0, 0)]); + + // Right existing + OptionLinkedMap::swap(5, 2); + assert_eq!(collect(), vec![(2, 2), (3, 3), (1, 1), (0, 0)]); + }); + } + #[test] fn linked_map_basic_insert_remove_should_work() { with_externalities(&mut new_test_ext(), || {