From 073040a053eb03f56c0393dfc2731106095e9da3 Mon Sep 17 00:00:00 2001
From: Gavin Wood <gavin@parity.io>
Date: Thu, 31 Oct 2019 09:24:23 +0100
Subject: [PATCH] Add translate API for storage values (#3947)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Add translate item.

* fix

* doc

* fix doc

* A test added.

* Apply suggestions from code review

Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com>

* address suggestion
---
 .../srml/support/src/storage/generator/mod.rs | 49 +++++++++++++++++++
 .../support/src/storage/generator/value.rs    | 19 ++++++-
 substrate/srml/support/src/storage/mod.rs     | 24 ++++++++-
 3 files changed, 90 insertions(+), 2 deletions(-)

diff --git a/substrate/srml/support/src/storage/generator/mod.rs b/substrate/srml/support/src/storage/generator/mod.rs
index ab7616158af..1bda7910237 100644
--- a/substrate/srml/support/src/storage/generator/mod.rs
+++ b/substrate/srml/support/src/storage/generator/mod.rs
@@ -30,3 +30,52 @@ pub use linked_map::{StorageLinkedMap, Enumerator, Linkage};
 pub use map::StorageMap;
 pub use double_map::StorageDoubleMap;
 pub use value::StorageValue;
+
+
+#[cfg(test)]
+#[allow(dead_code)]
+mod tests {
+	use runtime_io::TestExternalities;
+	use codec::Encode;
+	use crate::storage::{unhashed, generator::StorageValue};
+
+	struct Runtime {}
+	pub trait Trait {
+		type Origin;
+		type BlockNumber;
+	}
+
+	impl Trait for Runtime {
+		type Origin = u32;
+		type BlockNumber = u32;
+	}
+
+	decl_module! {
+		pub struct Module<T: Trait> for enum Call where origin: T::Origin {}
+	}
+
+	crate::decl_storage! {
+		trait Store for Module<T: Trait> as Runtime {
+			Value get(fn value) config(): (u64, u64);
+		}
+	}
+
+	#[test]
+	fn value_translate_works() {
+		let t = GenesisConfig::default().build_storage().unwrap();
+		TestExternalities::new(t).execute_with(|| {
+			// put the old value `1111u32` in the storage.
+			let key = Value::storage_value_final_key();
+			unhashed::put_raw(&key, &1111u32.encode());
+
+			// translate
+			let translate_fn = |old: Option<u32>| -> Option<(u64, u64)> {
+				old.map(|o| (o.into(), (o*2).into()))
+			};
+			let _ = Value::translate(translate_fn);
+
+			// new storage should be `(1111, 1111 * 2)`
+			assert_eq!(Value::get(), (1111, 2222));
+		})
+	}
+}
diff --git a/substrate/srml/support/src/storage/generator/value.rs b/substrate/srml/support/src/storage/generator/value.rs
index 8423503dde2..5ebc25a70af 100644
--- a/substrate/srml/support/src/storage/generator/value.rs
+++ b/substrate/srml/support/src/storage/generator/value.rs
@@ -16,7 +16,7 @@
 
 #[cfg(not(feature = "std"))]
 use rstd::prelude::*;
-use codec::{FullCodec, Encode, EncodeAppend, EncodeLike};
+use codec::{FullCodec, Encode, EncodeAppend, EncodeLike, Decode};
 use crate::{storage::{self, unhashed}, hash::{Twox128, StorageHasher}, traits::Len};
 
 /// Generator for `StorageValue` used by `decl_storage`.
@@ -60,6 +60,23 @@ impl<T: FullCodec, G: StorageValue<T>> storage::StorageValue<T> for G {
 		G::from_optional_value_to_query(value)
 	}
 
+	fn translate<O: Decode, F: FnOnce(Option<O>) -> Option<T>>(f: F) -> Result<Option<T>, ()> {
+		let key = Self::storage_value_final_key();
+
+		// attempt to get the length directly.
+		let maybe_old = match unhashed::get_raw(&key) {
+			Some(old_data) => Some(O::decode(&mut &old_data[..]).map_err(|_| ())?),
+			None => None,
+		};
+		let maybe_new = f(maybe_old);
+		if let Some(new) = maybe_new.as_ref() {
+			new.using_encoded(|d| unhashed::put_raw(&key, d));
+		} else {
+			unhashed::kill(&key);
+		}
+		Ok(maybe_new)
+	}
+
 	fn put<Arg: EncodeLike<T>>(val: Arg) {
 		unhashed::put(&Self::storage_value_final_key(), &val)
 	}
diff --git a/substrate/srml/support/src/storage/mod.rs b/substrate/srml/support/src/storage/mod.rs
index 648009b470e..5636d211990 100644
--- a/substrate/srml/support/src/storage/mod.rs
+++ b/substrate/srml/support/src/storage/mod.rs
@@ -17,7 +17,7 @@
 //! Stuff to do with the runtime's storage.
 
 use rstd::prelude::*;
-use codec::{FullCodec, FullEncode, Encode, EncodeAppend, EncodeLike};
+use codec::{FullCodec, FullEncode, Encode, EncodeAppend, EncodeLike, Decode};
 use crate::traits::Len;
 
 #[macro_use]
@@ -41,6 +41,28 @@ pub trait StorageValue<T: FullCodec> {
 	/// Load the value from the provided storage instance.
 	fn get() -> Self::Query;
 
+	/// Translate a value from some previous type (`O`) to the current type.
+	///
+	/// `f: F` is the translation function.
+	///
+	/// Returns `Err` if the storage item could not be interpreted as the old type, and Ok, along
+	/// with the new value if it could.
+	///
+	/// NOTE: This operates from and to `Option<_>` types; no effort is made to respect the default
+	/// value of the original type.
+	///
+	/// # Warning
+	///
+	/// This function must be used with care, before being updated the storage still contains the
+	/// old type, thus other calls (such as `get`) will fail at decoding it.
+	///
+	/// # Usage
+	///
+	/// This would typically be called inside the module implementation of on_initialize, while
+	/// ensuring **no usage of this storage are made before the call to `on_initialize`**. (More
+	/// precisely prior initialized modules doesn't make use of this storage).
+	fn translate<O: Decode, F: FnOnce(Option<O>) -> Option<T>>(f: F) -> Result<Option<T>, ()>;
+
 	/// Store a value under this key into the provided storage instance.
 	fn put<Arg: EncodeLike<T>>(val: Arg);
 
-- 
GitLab