3

So the question is quite simple. Let's say I have an original definition of nested enums like so:


#[derive(MaxEncodedLen, Debug, Encode, Decode, Copy, Clone, Eq, PartialEq, TypeInfo)]
pub enum MyNestedEnum {
    Thing1(A),
    Thing2(B),
}

#[derive(MaxEncodedLen, Debug, Encode, Decode, Copy, Clone, Eq, PartialEq, TypeInfo)]
pub enum A {
    AThing1,
    AThing2,
    AThing3,
}

#[derive(MaxEncodedLen, Debug, Encode, Decode, Copy, Clone, Eq, PartialEq, TypeInfo)]
pub enum B {
    BThing1,
}

And this enum is referenced for used in storage:

    #[pallet::storage]
    #[pallet::getter(fn account_enum_unit)]
    pub type AccountEnumUnit<T: Config> = StorageDoubleMap<
        _, 
        Blake2_128Concat, T::AccountId, 
        Blake2_128Concat, MyNestedEnum,
        SomeUnit,
        >;

Now, assume that the chain has been in operation and some data has been stored, using for example one of the paths to the element:

MyNestedEnum::Thing2(B::BThing1)

But now I want to upgrade my chain so that enum B is enhanced with another new element changing it to:

pub enum B {
    BThing1,
    BThing2,
}

...and in future I should be able to store some new data using the new element, MyNestedEnum::Thing2(B::BThing2)

As the structure of enum B has changed, must I perform a storage migration during the upgrade or is it sufficient to simply add the new element at the end in the enum? In other words will adding this new element corrupt storage even if it has not been used yet?

As a general remark I am assuming that as long as elements are not removed, no migration would need to happen, and also no migration would need to happen if you can be sure that an element nested or otherwise has never been used/stored at any time.

There are many other possibilities for change that could happen, but I wondered what the experience was for others.

1 Answer 1

2

If you don't need to change the existing items then you don't need a migration.


Actually, you could write some tests yourself. It's easy to verify that.

use parity_scale_codec::{Decode, Encode};

#[derive(Encode, Decode)]
enum A {
    B(B),
    C(C),
}
#[derive(Encode, Decode)]
enum B {
    Ba,
    Bb,
    Bc,
}
#[derive(Encode, Decode)]
enum C {
    Ca,
}

#[derive(Encode, Decode)]
enum NewA {
    B(B),
    NewC(NewC),
}
#[derive(Encode, Decode)]
enum NewC {
    Ca,
    Cb,
}

fn main() {
    let a_ba = A::B(B::Ba);
    let new_a_ba = NewA::B(B::Ba);

    assert_eq!(a_ba.encode(), new_a_ba.encode());

    let a_ca = A::C(C::Ca);
    let new_a_ca = NewA::NewC(NewC::Ca);

    assert_eq!(a_ca.encode(), new_a_ca.encode());
}
1
  • For anyone looking this is actually a comprehensive answer because it covers other cases not mentioned in the question like swapping out one enum for another. In this case both the old and the new enums are equivalent in how they are represented in storage (and memory) even if the names change.
    – T9b
    Oct 6, 2022 at 20:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.