## Module frame_runtime_upgrades_and_migrations

Source
Expand description

Learn about Runtime Upgrades and best practices for writing Migrations.

## §Runtime Upgrades

At their core, blockchain logic consists of

1. on-chain state,
2. a state transition function.

In Bizinikiwi-based blockchains, state transition functions are referred to as runtimes.

Traditionally, before Bizinikiwi, upgrading state transition functions required node operators to download new software and restart their nodes in a process called forking.

Bizinikiwi-based blockchains do not require forking, and instead upgrade runtimes in a process called “Runtime Upgrades”.

Forkless runtime upgrades are a defining feature of the Bizinikiwi framework. Updating the runtime logic without forking the code base enables your blockchain to seamlessly evolve over time in a deterministic, rules-based manner. It also removes ambiguity for node operators and other participants in the network about what is the canonical runtime.

This capability is possible due to the runtime of a blockchain existing in on-chain storage.

### §Performing a Runtime Upgrade

To upgrade a runtime, an `Origin` with the necessary permissions (usually via governance) changes the `:code` storage. Usually, this is performed via a call to `set_code` (or `set_code_without_checks`) with the desired new runtime blob, scheduled using [`pezpallet_scheduler`].

Prior to building the new runtime, don’t forget to update the `RuntimeVersion`.

## §Migrations

It is often desirable to define logic to execute immediately after runtime upgrades (see this diagram).

Self-contained pieces of logic that execute after a runtime upgrade are called “Migrations”.

The typical use case of a migration is to ‘migrate’ pezpallet storage from one layout to another, for example when the encoding of a storage item is changed. However, they can also execute arbitrary logic such as:

- Calling arbitrary pezpallet methods.
- Mutating arbitrary on-chain state.
- Cleaning up some old storage items that are no longer needed.

### §Single Block Migrations

- Execute immediately and entirely at the beginning of the block following a runtime upgrade.
- Are suitable for migrations which are guaranteed to not exceed the block weight.
- Are simply implementations of `OnRuntimeUpgrade`.

To learn best practices for writing single block pezpallet storage migrations, see the Single Block Migration Example Pezpallet.

#### §Scheduling the Single Block Migrations to Run Next Runtime Upgrade

Schedule migrations to run next runtime upgrade passing them as a parameter to your `Config` pezpallet:

ⓘ

    /// Tuple of migrations (structs that implement `OnRuntimeUpgrade`)
    type Migrations = (
    	pezpallet_example_storage_migration::migrations::v1::versioned::MigrateV0ToV1,
    	MyCustomMigration,
    	// ...more migrations here
    );
    impl pezframe_system::Config for Runtime {
    	type SingleBlockMigrations = Migrations;
    }

#### §Ensuring Single Block Migration Safety

“My migration unit tests pass, so it should be safe to deploy right?”

No! Unit tests execute the migration in a very simple test environment, and cannot account for the complexities of a real runtime or real on-chain state.

Prior to deploying migrations, it is critical to perform additional checks to ensure that when run in our real runtime they will not brick the chain due to:

- Panicking.
- Touching too many storage keys and resulting in an excessively large PoV.
- Taking too long to execute.

`try-runtime-cli` has a sub-command `on-runtime-upgrade` which is designed to help with exactly this.

Developers MUST run this command before deploying migrations to ensure they will not inadvertently result in a bricked chain.

It is recommended to run as part of your CI pipeline. See the pezkuwi-sdk check-runtime-migration job for an example of how to configure this.

#### §Note on the Manipulability of PoV Size and Execution Time

While `try-runtime-cli` can help ensure with very high certainty that a migration will succeed given existing on-chain state, it cannot prevent a malicious actor from manipulating state in a way that will cause the migration to take longer or produce a PoV much larger than previously measured.

Therefore, it is important to write migrations in such a way that the execution time or PoV size it adds to the block cannot be easily manipulated. e.g., do not iterate over storage that can quickly or cheaply be bloated.

If writing your migration in such a way is not possible, a multi block migration should be used instead.

#### §Other useful tools

`Chopsticks` is another tool in the Bizinikiwi ecosystem which developers may find useful to use in addition to `try-runtime-cli` when testing their single block migrations.

### §Multi Block Migrations

Safely and easily execute long-running migrations across multiple blocks.

Suitable for migrations which could use arbitrary amounts of block weight.

See the multi-block-migrations example for reference.