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

Assert Storage Key & Value Invariance #13037

Closed
@mustermeiszer

Description

@mustermeiszer

The reason for this issue is that silent changes in the scale-coding of types -- through changes in the types themselves, or unlikely, changes in the codec -- used for storage keys and values worry me more and more. Even more, since the latest migrations in Substrate mostly were triggered via OnRuntimeUpgrade instead of hooks.

Proposal

My proposal for a solution would be the following. I have not coded this already, just checked that the traits work and the static assertion fails, but I am not sure if an auto-derive is trivial and so forth.

  • Codec provides 2 additional traits + auto-derive for StaticEncode

    #[const_trait]
    trait StaticEncode<const BYTES: usize> {
        fn zeroed_and_uninit() -> [u8; BYTES];
    }
    
    #[const_trait]
    trait Lock<T: ~const StaticEncode<BYTES>, const BYTES: usize> {
        const LOCK: [u8; BYTES];
        
        fn check_lock() {
            let current = T::zeroed_and_uninit();
            let lock = Self::LOCK;
            
            let mut i = 0;
            while i < BYTES {
                assert!(current[i] == lock[i], "Static encoding does not match lock!");
                i += 1;
            }
        }
    }
  • Storage items of pallets automatically implement the following impl block
    Users can opt-out of this by adding pallet::without_storage_lock

    // Assuming `StorageMap<T::Key, T::Value>`
    impl<T: Config>  Pallet<T> 
    where 
       T: Lock<<T as Config>::Key, BYTES>,
       T: Lock<<T as Config>::Value, BYTES>,
    {
       const _: () =  { <T as Lock<T as Config>::Key, BYTES>::check_lock(); };
       const _: () =  { <T as Lock<T as Config>::Value, BYTES>::check_lock(); };
    } 
  • Runtimes must implement Lock<T> for every T that is a key or a value

Advantages

  • Compilation ensures that codec of storage relevant types has not changed
  • Dependencies are also checked automatically → no silent errors here

Problems

  • Getting the BYTES for associated types in pallets needs to be constant but runtime specific
  • Overhead for runtimes
  • Is there a "static" representation for every codec object that can be storage key or value?
    For enums it would be needed to actually encode all variants in the "static" representation in order to ensure no variant has changed its encoding.
    • Other relevant things to pay attention to?

Edits:

  • Typos
  • Changed T (i.e. the runtime) to implement the locks

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions