Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.

Add translate function for storage prefixed map. #4555

Merged
merged 4 commits into from
Jan 13, 2020
Merged
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 68 additions & 1 deletion frame/support/src/storage/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -403,17 +403,20 @@ pub trait StoragePrefixedMap<Value: FullCodec> {
/// Storage prefix. Used for generating final key.
fn storage_prefix() -> &'static [u8];

/// Final full prefix that prefixes all keys.
fn final_prefix() -> [u8; 32] {
let mut final_key = [0u8; 32];
final_key[0..16].copy_from_slice(&Twox128::hash(Self::module_prefix()));
final_key[16..32].copy_from_slice(&Twox128::hash(Self::storage_prefix()));
final_key
}

/// Remove all value of the storage.
fn remove_all() {
sp_io::storage::clear_prefix(&Self::final_prefix())
}

/// Iter over all value of the storage.
fn iter() -> PrefixIterator<Value> {
let prefix = Self::final_prefix();
PrefixIterator {
Expand All @@ -422,6 +425,51 @@ pub trait StoragePrefixedMap<Value: FullCodec> {
phantom_data: Default::default(),
}
}

/// Translate the values from some previous `OldValue` to the current type.
///
/// `TV` translates values.
///
/// Returns `Err` if the map could not be interpreted as the old type, and Ok if it could.
/// The `Err` contains the number of value that couldn't be interpreted, those value are
/// removed from the map.
///
/// # 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_values<OldValue, TV>(translate_val: TV) -> Result<(), u32>
where OldValue: Decode, TV: Fn(OldValue) -> Value
{
let prefix = Self::final_prefix();
let mut previous_key = prefix.to_vec();
let mut errors = 0;
while let Some(next_key) = sp_io::storage::next_key(&previous_key)
.filter(|n| n.starts_with(&prefix[..]))
{
if let Some(value) = unhashed::get(&next_key) {
unhashed::put(&next_key[..], &translate_val(value));
} else {
// We failed to read the value. Remove the key and increment errors.
unhashed::kill(&next_key[..]);
errors += 1;
}

previous_key = next_key;
}

if errors == 0 {
Ok(())
} else {
Err(errors)
}
}
}

#[cfg(test)]
Expand Down Expand Up @@ -463,6 +511,7 @@ mod test {
let k = [twox_128(b"MyModule"), twox_128(b"MyStorage")].concat();
assert_eq!(MyStorage::final_prefix().to_vec(), k);

// test iteration
assert_eq!(MyStorage::iter().collect::<Vec<_>>(), vec![]);

unhashed::put(&[&k[..], &vec![1][..]].concat(), &1u64);
Expand All @@ -472,9 +521,27 @@ mod test {

assert_eq!(MyStorage::iter().collect::<Vec<_>>(), vec![1, 2, 3, 4]);

// test removal
MyStorage::remove_all();

assert_eq!(MyStorage::iter().collect::<Vec<_>>(), vec![]);

// test migration
unhashed::put(&[&k[..], &vec![1][..]].concat(), &1u128);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You inserting a u128 and use u32 in the translate function. You should actually store a u32.

And then call iter() to make sure that an empty vec is returned.

Than you should to the translation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh yes I updated tests

unhashed::put(&[&k[..], &vec![8][..]].concat(), &2u128);

MyStorage::translate_values(|v: u32| v as u64).unwrap();
assert_eq!(MyStorage::iter().collect::<Vec<_>>(), vec![1, 2]);

// test migration 2
unhashed::put(&[&k[..], &vec![1][..]].concat(), &1u128);
unhashed::put(&[&k[..], &vec![1, 1][..]].concat(), &3u64);
unhashed::put(&[&k[..], &vec![8][..]].concat(), &2u128);

assert_eq!(MyStorage::translate_values(|v: u128| v as u64), Err(1));
assert_eq!(MyStorage::iter().collect::<Vec<_>>(), vec![1, 2]);


// test that other values are not modified.
assert_eq!(unhashed::get(&key_before[..]), Some(32u64));
assert_eq!(unhashed::get(&key_after[..]), Some(33u64));
});
Expand Down