Skip to content
This repository has been archived by the owner on Aug 20, 2020. It is now read-only.

Add tutorial to perform forkless runtime upgrade #26

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 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
14 changes: 14 additions & 0 deletions tuts/perform-a-runtime-upgrade/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"slug": "config",
"lang": "en",
"title": "Perform a Runtime Upgrade",
"excerpt": "Forklessly upgrade your chain's runtime logic.",
"tags": [ "Medium", "2 Hours", "Prerequisites" ],
"versions": [ "v2.0.0-alpha.6" ],
"menu": [
{ "title": "Overview", "slug": "index" },
{ "title": "Code Changes", "slug": "code" },
{ "title": "Upgrade Transaction", "slug": "transaction" },
{ "title": "Storage Migrations", "slug": "migration" }
]
}
Binary file added tuts/perform-a-runtime-upgrade/featured-image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
148 changes: 148 additions & 0 deletions tuts/perform-a-runtime-upgrade/v2.0.0-alpha.6/code.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
---
slug: code
lang: en
title: Code Changes
---

## Install the Node Template

You should already have version `v2.0.0-alpha.6` of the [Substrate Node
Template](https://github.com/substrate-developer-hub/substrate-node-template) compiled and running on your
computer from when you completed the [Create Your First Substrate Chain
Tutorial](/tutorials/create-your-first-substrate-chain/v2.0.0-alpha.6). If you do not, please complete that
tutorial.

> Experienced developers who truly prefer to skip that tutorial, you may install the node template according to the instructions in its readme.

Further, you should have the chain running, as it was at the end of that tutorial.

If you have completed that tutorial, but your chain is no longer currently running, `cd` into the directory you created in that tutorial, and run the command `./target/release/node-template --dev` to start your node again.

## Make a Change to the Code

Runtime upgrades are necessary when you want to change the code of a live chain. While it is generally advisale to complete the code as much as possible before launching the chain, changes after launch become necessary to do things like fix bugs or add features.

### Primary Logic Change

The Substrate Node Template contains a template pallet which accepts transactions to store a value and increment a value. In this tutorial we will add one extrinsic to clear the value, and leave `None` in its place.

Open the `substrate-node-template` in your favorite code editor. Then open the file
`pallets/template/src/lib.rs`

```
substrate-node-template
|
+-- runtime
|
+-- pallets
| |
| +-- template
| |
| +-- Cargo.toml
| |
| +-- src
| |
| +-- lib.rs <-- Edit this file
| |
| +-- mock.rs
| |
| +-- tests.rs
|
+-- scripts
|
+-- node
|
+-- ...
```

In this file you will see this block of code where the two extrinsics to set the value and increment the value are written.

```rust
decl_module! {
/// The module declaration.
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
// --snip--

#[weight = frame_support::weights::SimpleDispatchInfo::default()]
pub fn do_something(origin, something: u32) -> dispatch::DispatchResult {
// --snip--
}

#[weight = frame_support::weights::SimpleDispatchInfo::default()]
pub fn cause_error(origin) -> dispatch::DispatchResult {
// --snip--
}

// TODO Add your code here.
}
}
```

Add the following code at the bottom of the `decl_module!` block. This code adds a third extrinsic to clear the stored value.

```rust
/// Clears the value stored, if any
#[weight = frame_support::weights::SimpleDispatchInfo::default()]
pub fn clear_value(origin) -> dispatch::DispatchResult {
// Check it was signed and get the signer.
let _who = ensure_signed(origin)?;

// Clear the storage value
Something::kill();

Ok(())
}
```

Confirm that your changes are correct so far by running `cargo check -p pallet-template`. If this command completes successfully, you're ready to move on. If not, stop and solve your errors or ask for help before continuing.

### Bumping the Spec Version

We've already made all of the logic changes we intend to make to our code, and our runtime is perfectly valid in its current state. However, because we will be upgrading a live chain, we need to indicate that this is a new version of the runtime. In particular, it is a new `spec_version`.

> There are multiple ways in which a runtime is versioned. Read more about runtime versioning in the rustdocs about the [`RuntimeVersion` struct](https://substrate.dev/rustdocs/v2.0.0-alpha.6/sp_version/struct.RuntimeVersion.html).

Open the file
`runtime/src/lib.rs`

```
substrate-node-template
|
+-- runtime
| |
| +-- Cargo.toml
| |
| +-- src
| |
| +-- lib.rs <-- Edit this file
|
+-- pallets
|
+-- scripts
|
+-- node
|
+-- ...
```

Look for this section of code, and change the spec version from 1 to 2.

```rust
/// This runtime version.
pub const VERSION: RuntimeVersion = RuntimeVersion {
spec_name: create_runtime_str!("node-template"),
impl_name: create_runtime_str!("node-template"),
authoring_version: 1,
spec_version: 1, //TODO Change this to 2
impl_version: 1,
apis: RUNTIME_API_VERSIONS,
};
```

## Recompile Your Runtime

You're now ready to recompile your runtime. To build _just_ the runtime, and not the entire node, you may run the command.

```bash
cargo build --release -p node-template-runtime
```
31 changes: 31 additions & 0 deletions tuts/perform-a-runtime-upgrade/v2.0.0-alpha.6/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
slug: index
lang: en
title: Overview
---

In this tutorial, you will perform a runtime upgrade on a live blockchain. You will use Substrate's forkless upgrade mechanism to perform the upgrade without requiring a client upgrade, and without causing a fork in the network.

This tutorial guides you step-by-step through reproducing the results demonstrated by Ricardo Rius in his [Sub0 talk, Runtime Upgrades](https://www.crowdcast.io/e/sub0-online/8).

We only expect that:

* You are generally familiar with software development, writing code, and running your code.
* You have completed the [Create Your First Substrate Chain Tutorial](/tutorials/create-your-first-substrate-chain/v2.0.0-aplha.6).
* You are open to learning about the bleeding edge of blockchain development.

If you run into an issue on this tutorial, **we are here to help!** You can [create a new
issue](https://github.com/substrate-developer-hub/tutorials/issues/new) or
contact us on [Riot](https://riot.im/app/#/room/!HzySYSaIhtyWrwiwEV:matrix.org).

## What you will be doing

Before we even get started, let's lay out what we are going to do over the course of this tutorial.
We will:

1. Launch a blockchain based on a template project.
2. Modify this template project with some custom logic, after the chain has already launched.
3. Upgrade the live chain using Substrate's forkless upgrade mechanism.
4. Discuss Chain state migrations.

Sound reasonable? Good, then let's begin!
72 changes: 72 additions & 0 deletions tuts/perform-a-runtime-upgrade/v2.0.0-alpha.6/migrations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
---
slug: migrations
lang: en
title: Storage Migrations
---

The runtime upgrade we just performed completed successfully simply by adding our new feature and incrementing our `spec_version`. In may simple and moderately complex cases this is all that's necessary. In other cases, you may restructure the way data is stored in the blockchain as well as modifying the logic. In these cases, it is necessary to include a migration path for the existing data. In this section we'll explore data migrations.

## Primary Code Change

For this example we will rename our storage struct from `TemplateModule` to `UpgradedTemplateModule`. To begin we open the `pallets/template/src/lib.rs` file and modify the the following line
```rust
trait Store for Module<T: Trait> as TemplateModule {
```

so that it says

```rust
trait Store for Module<T: Trait> as UpgradedTemplateModule {
```

## Bumping the Spec Version

As before, we will open the `runtime/src/lib.rs` file and change our `spec_version`. This time we change it to 3.
```rust
/// This runtime version.
pub const VERSION: RuntimeVersion = RuntimeVersion {
spec_name: create_runtime_str!("node-template"),
impl_name: create_runtime_str!("node-template"),
authoring_version: 1,
spec_version: 2, //TODO change this to 3
impl_version: 1,
apis: RUNTIME_API_VERSIONS,
};
```

## Writing the Migration

Any time you change the storage struct, the name of a storage item, or the way a key is calculated in a storage map, you need to write a migration, or else the old data will not be accessible by the new code.

As you can see from the `decl_storage!` block of our template pallet, we are only dealing with a single storage item that was, and still is called `Something`.

The complete code for our migration looks like this, and can be inserted at the bottom of the `decl_module!` block, just like the dispatchable call we aded in the previous upgrade.

```rust
fn on_runtime_upgrade() -> frame_support::weights::Weight {
use frame_support::storage::migration::{get_storage_value, put_storage_value};

let value_to_migrate: Option<u32> = get_storage_value(b"TempalteModule", b"Something", &[]);
put_storage_value(b"UpgradedTemplateModule", b"Something", &[], value_to_migrate);

1_000 // In reality the weight of migration should be determined by benchmarking
}
```

First, his code `use`s two helper functions from frame support that are designed specifically for storage migrations. You will always need to do this.

Next, it grabs the value out of the old storage location. Notice that we need an explicit type annotation when grabbing the old data. This will always be necessary, and if you are unsure, just check the `decl_storage!` block to learn the type. As parameters, we have supplied the _old_ storage struct name, the storage item name, and an empty array. The third parameter will be necessary when working with storage maps, but we are using a plain storage value, so we leave it blank.

Once the old value is retrieved, we put it into the new storage locations. This time we supply the _new_ storage struct name, the same storage value name, and, again, an empty array. The final parameter if the value to store that we grabbed out of the old storage location.

TODO Do we need to kill the old storage here? Probably.

Finally we return a weight to quantify the time it takes to execute this logic. In this tutorial I have included an arbitrary weight. But in a real network, it is important to benchmark the execution time of the function, and choose a weight appropriately.

## Perform the Upgrade

You're now ready to recompile your runtime, and perform the upgrade using the sudo pallet as we did with the previous upgrade.

## That's it!

In this tutorial, you performed two runtime upgrades on a live blockchain without causing any forks. Congratulations! You also learned how and when to write storage migrations.
35 changes: 35 additions & 0 deletions tuts/perform-a-runtime-upgrade/v2.0.0-alpha.6/transaction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
slug: transaction
lang: en
title: Upgrade Transaction
---

In this section we will perform the on-chain runtime upgrading by submitting the new runtime logic in an extrinsic.

## Locating the Wasm Build Artifact

At the end of the last section we compiled our runtime. Substrate Runtimes are always compiles to Web Assembly (or Wasm) as well as native code so that the Wasm can be stored on the blockchain and facilitate this forkless upgrade process. Our freshly compiled runtime is stored at `./target/release/wbuild/node-template-runtime/node-template-runtime.compact.wasm`. Ensure this file exists and that it was modified recently.

## Starting the User Interface

For this upgrade, we will use the Polkadot JS Apps user interface; the same UI we used in the [Create Your First Substrate Chain](/tutorials/create-your-first-substrate-chain/v2.0.0-alpha.6) Tutorial.

In your web browser, navigate to [https://polkadot.js.org/apps](https://polkadot.js.org/apps/#/settings?rpc=ws://127.0.0.1:9944).

On the `Settings` tab ensure that you are connected to a `Local Node` or `ws://127.0.0.1:9944`.

> Some browsers, notably Firefox, will not connect to a local node from an https website. An easy work around is to try another browser, like Chromium. Another option is to [host this interface locally](https://github.com/polkadot-js/apps#development).

## Submit the Transaction

As you can imagine, in a real-world blockchain, we don't want just anyone to be able to change the runtime logic. There is a special transaction for performing these upgrades called, `system::set_code`. This special transaction cannot be called by an ordinary user, and must be called from within the blockchain itself. Substrate provides many useful pallets to provide limited access to this sensitive function as well as others like it. In a real-world blockchain you might use the Democracy or Collective pallets. Our blockchain has a very simple governance mechanism called sudo which allows a privileged user to call sensitive functions like `system::set_code`. In our case, the privileged user is the `Alice` account, so we will submit the upgrade transaction as her.

Navigate to the Sudo tab in the interface, and select `system` and `set_code` from the dropdowns. Upload the `node-template-runtime.compact.wasm` file we saw previously, and submit the transaction.

TODO screenshot

## Try out the Results

Once the upgrade transaction is included in a block, you should see a notification in the UI saying the a runtime upgrade has been performed, and the UI will need to be refreshed. Once you've refreshed, you can navigate to the "Extrinsics" tab, select "Template Module" from the dropdown, and see that our new extrinsic, `clear_value` is now available.

Congratulations, you've upgraded your blockchain's runtime! Traditionally the process of upgrading a blockchain would have required a coordinated effort from all (or at least most) of the node operators in the network. But in this case, you have performed the upgrade in a single transaction without even causing a fork!
Copy link

Choose a reason for hiding this comment

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

This is a tricky one. On the one hand I think the text is good and emphasizes the point of the tutorial: substrate fixes the whole hard fork mess. On the other hand though it almost seems like sudo is what makes this so easy, which is not the correct message.
The text clearly mentions Democracy and Collective above but I think it's worth repeating here that for a production blockchain, upgrading the runtime is not something a single entity can do at a whim?

I am not sure that newcomers to the blockchain space fully realize what a clusterfuck hard forks are in classical blockchains and how hard it is to get right.