Data Migration
When the chain needs to be upgraded after its runtime logic is modified, it may involve changes in the data storage structure. In this case, users need to define a data migration logic to store the old data in the new structure.
Storage migration
Storage migrations are user-defined, one-time functions that allow developers to rework existing storage in order to convert it to conform to updated expectations. For instance, imagine a runtime upgrade that changes the data type used to represent user balances from an unsigned integer to a signed integer - in this case, the storage migration would read the existing value as an unsigned integer and write back an updated value that has been converted to a signed integer. Failure to perform a storage migration when needed will result in the runtime execution engine misinterpreting the storage values that represent the runtime state and lead to undefined behavior.
Framework implementation
FRAME storage migrations are implemented by way of the OnRuntimeUpgrade
trait, which specifies a single function on_runtime_upgrade
. This function provides a hook that allows runtime developers to specify logic that will run immediately after a runtime upgrade but before any on_initialize
function has executed.
The logic and complexity of each migration vary in the needs:
When mitigating deprecated storageMaps, you need to define
generate_storage_alias!
macros for the old storageMaps as follows:
Audit
is the name of the pallet, UnVerifyProof
is the name of the Storage, and the Map
corresponds to the StorgaeMap type. The generic parameters in the Map
are the old primary keys and values.
Use version control to make migration processing more secure. Declare the version in Pallet as follows:
Then use the #[pallet::storage_version()]
macro to declare the version for the current pallet:
Through version comparison, call the corresponding migration logic:
The order of migration execution will in the order in which the pallets appear in
construct_runtime!
macro. The order in which the upgrade occurs is in reverse order.Declare the final migration object in runtime and execute it in the next upgrade:
Testing Migrations
It is important to test storage migrations and a number of utilities exist in Substrate to assist in this process:
The Substrate Debug Kit includes a Remote Externalities tool that allows storage migration unit testing to be safely performed on live chain data.
The fork-off-substrate script makes it easy to create a chain specification that can be used to bootstrap a local test chain for testing runtime upgrades and storage migrations.
In the OnRuntimeUpgrade trait, two functions pre_upgrade
and post_upgrade
are defined for the use of testing.
For a more specific approach, please refer to the official Substrate migration examples.