diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index 90085ce801..3cb4912571 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -79,7 +79,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // implementation changes and behavior does not, then leave spec_version as // is and increment impl_version. spec_version: 197, - impl_version: 197, + impl_version: 198, apis: RUNTIME_API_VERSIONS, }; diff --git a/substrate/frame/support/src/lib.rs b/substrate/frame/support/src/lib.rs index bdcde0f159..0a7a967db8 100644 --- a/substrate/frame/support/src/lib.rs +++ b/substrate/frame/support/src/lib.rs @@ -292,6 +292,36 @@ mod tests { }); } + #[test] + fn double_map_swap_works() { + new_test_ext().execute_with(|| { + DataDM::insert(0, 1, 1); + DataDM::insert(1, 0, 2); + DataDM::insert(1, 1, 3); + + let get_all = || vec![ + DataDM::get(0, 1), + DataDM::get(1, 0), + DataDM::get(1, 1), + DataDM::get(2, 0), + DataDM::get(2, 1), + ]; + assert_eq!(get_all(), vec![1, 2, 3, 0, 0]); + + // Two existing + DataDM::swap(0, 1, 1, 0); + assert_eq!(get_all(), vec![2, 1, 3, 0, 0]); + + // Left existing + DataDM::swap(1, 0, 2, 0); + assert_eq!(get_all(), vec![2, 0, 3, 1, 0]); + + // Right existing + DataDM::swap(2, 1, 1, 1); + assert_eq!(get_all(), vec![2, 0, 0, 1, 3]); + }); + } + #[test] fn linked_map_basic_insert_remove_should_work() { new_test_ext().execute_with(|| { diff --git a/substrate/frame/support/src/storage/generator/double_map.rs b/substrate/frame/support/src/storage/generator/double_map.rs index 28378034b2..8917cbf952 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 rstd::prelude::*; use rstd::borrow::Borrow; use codec::{Ref, FullCodec, FullEncode, Encode, EncodeLike, EncodeAppend}; -use crate::{storage::{self, unhashed}, hash::{StorageHasher, Twox128}}; +use crate::{storage::{self, unhashed}, hash::{StorageHasher, Twox128}, traits::Len}; /// Generator for `StorageDoubleMap` used by `decl_storage`. /// @@ -137,6 +137,29 @@ where G::from_optional_value_to_query(value) } + fn swap(x_k1: XKArg1, x_k2: XKArg2, y_k1: YKArg1, y_k2: YKArg2) + where + XKArg1: EncodeLike, + XKArg2: EncodeLike, + YKArg1: EncodeLike, + YKArg2: EncodeLike + { + let final_x_key = Self::storage_double_map_final_key(x_k1, x_k2); + let final_y_key = Self::storage_double_map_final_key(y_k1, y_k2); + + let v1 = unhashed::get_raw(&final_x_key); + if let Some(val) = unhashed::get_raw(&final_y_key) { + unhashed::put_raw(&final_x_key, &val); + } else { + unhashed::kill(&final_x_key) + } + if let Some(val) = v1 { + unhashed::put_raw(&final_y_key, &val); + } else { + unhashed::kill(&final_y_key) + } + } + fn insert(k1: KArg1, k2: KArg2, val: VArg) where KArg1: EncodeLike, @@ -225,4 +248,21 @@ where Self::append(Ref::from(&k1), Ref::from(&k2), items.clone()) .unwrap_or_else(|_| Self::insert(k1, k2, items)); } + + fn decode_len(key1: KArg1, key2: KArg2) -> Result + where KArg1: EncodeLike, + KArg2: EncodeLike, + V: codec::DecodeLength + Len, + { + let final_key = Self::storage_double_map_final_key(key1, key2); + if let Some(v) = unhashed::get_raw(&final_key) { + ::len(&v).map_err(|e| e.what()) + } else { + let len = G::from_query_to_optional_value(G::from_optional_value_to_query(None)) + .map(|v| v.len()) + .unwrap_or(0); + + Ok(len) + } + } } diff --git a/substrate/frame/support/src/storage/mod.rs b/substrate/frame/support/src/storage/mod.rs index 88818b84e2..7c8ce9c24f 100644 --- a/substrate/frame/support/src/storage/mod.rs +++ b/substrate/frame/support/src/storage/mod.rs @@ -284,6 +284,14 @@ pub trait StorageDoubleMap { KArg1: EncodeLike, KArg2: EncodeLike; + /// Swap the values of two key-pairs. + fn swap(x_k1: XKArg1, x_k2: XKArg2, y_k1: YKArg1, y_k2: YKArg2) + where + XKArg1: EncodeLike, + XKArg2: EncodeLike, + YKArg1: EncodeLike, + YKArg2: EncodeLike; + fn insert(k1: KArg1, k2: KArg2, val: VArg) where KArg1: EncodeLike, @@ -330,4 +338,17 @@ pub trait StorageDoubleMap { V: EncodeAppend, Items: IntoIterator + Clone + EncodeLike, Items::IntoIter: ExactSizeIterator; + + /// Read the length of the value in a fast way, without decoding the entire value. + /// + /// `V` is required to implement `Codec::DecodeLength`. + /// + /// Note that `0` is returned as the default value if no encoded value exists at the given key. + /// Therefore, this function cannot be used as a sign of _existence_. use the `::exists()` + /// function for this purpose. + fn decode_len(key1: KArg1, key2: KArg2) -> Result + where + KArg1: EncodeLike, + KArg2: EncodeLike, + V: codec::DecodeLength + Len; } diff --git a/substrate/frame/support/test/tests/decl_storage.rs b/substrate/frame/support/test/tests/decl_storage.rs index 396288d3be..cdd99275a6 100644 --- a/substrate/frame/support/test/tests/decl_storage.rs +++ b/substrate/frame/support/test/tests/decl_storage.rs @@ -521,6 +521,10 @@ mod test_append_and_len { MapVecWithDefault: map u32 => Vec = vec![6, 9]; OptionMapVec: map u32 => Option>; + DoubleMapVec: double_map u32, blake2_256(u32) => Vec; + DoubleMapVecWithDefault: double_map u32, blake2_256(u32) => Vec = vec![6, 9]; + OptionDoubleMapVec: double_map u32, blake2_256(u32) => Option>; + LinkedMapVec: linked_map u32 => Vec; LinkedMapVecWithDefault: linked_map u32 => Vec = vec![6, 9]; OptionLinkedMapVec: linked_map u32 => Option>; @@ -596,11 +600,13 @@ mod test_append_and_len { OptionVec::put(&vec![1, 2, 3, 4, 5]); MapVec::insert(1, &vec![1, 2, 3, 4, 5, 6]); LinkedMapVec::insert(2, &vec![1, 2, 3]); + DoubleMapVec::insert(0, 1, &vec![1, 2]); assert_eq!(JustVec::decode_len().unwrap(), 4); assert_eq!(OptionVec::decode_len().unwrap(), 5); assert_eq!(MapVec::decode_len(1).unwrap(), 6); assert_eq!(LinkedMapVec::decode_len(2).unwrap(), 3); + assert_eq!(DoubleMapVec::decode_len(0, 1).unwrap(), 2); }); } @@ -636,6 +642,16 @@ mod test_append_and_len { assert_eq!(OptionLinkedMapVec::get(0), None); assert_eq!(OptionLinkedMapVec::decode_len(0), Ok(0)); + + // Double map + assert_eq!(DoubleMapVec::get(0, 0), vec![]); + assert_eq!(DoubleMapVec::decode_len(0, 1), Ok(0)); + + assert_eq!(DoubleMapVecWithDefault::get(0, 0), vec![6, 9]); + assert_eq!(DoubleMapVecWithDefault::decode_len(0, 1), Ok(2)); + + assert_eq!(OptionDoubleMapVec::get(0, 0), None); + assert_eq!(OptionDoubleMapVec::decode_len(0, 1), Ok(0)); }); } }