Substrate Developer Hub

Substrate Developer Hub

  • Tutorials
  • Knowledge Base
  • Recipes
  • API Reference
  • Languages iconEnglish
    • 简体中文
    • Help Translate

›Perform a Forkless Upgrade

Perform a Forkless Upgrade

  • Introduction
  • Use the Sudo Pallet
  • Schedule an Upgrade
Edit

Use the Sudo Pallet

As the name of the Sudo pallet implies, it provides capabilities related to the management of a single sudo ("superuser do") administrator. In FRAME, the Root Origin is used to identify the runtime administrator; some of FRAME's features, including the ability to update the runtime by way of the set_code function, are only accessible to this administrator. The Sudo pallet maintains a single storage item: the ID of the account that has access to the pallet's dispatchable functions. The Sudo pallet's sudo function allows the holder of this account to invoke a dispatchable as the Root origin. The following pseudo-code demonstrates how this is achieved, refer to the Sudo pallet's source code to learn more.

fn sudo(origin, call) -> Result {
  // Ensure caller is the account identified by the administrator key
  let sender = ensure_signed(origin)?;
  ensure!(sender == Self::key(), Error::RequireSudo);

  // Dispatch the specified call as the Root origin
  let res = call.dispatch(Origin::Root);
  Ok()
}

Start the Template Node

Since forkless runtime upgrades do not require network participants to restart their blockchain clients, the first step of this tutorial is to start the template node as-is. Build and start the unmodified Node Template. The node will not be restarted as part of this tutorial despite the fact that two runtime upgrades are performed.

WASM_BUILD_TOOLCHAIN=nightly-2020-10-05 cargo run --release -- --dev --tmp

Notice that the well-known Alice account is configured as the holder of the Sudo pallet's key in the development_config function of the template node's chain specification file - this is the configuration that is used when the node is launched with the --dev flag. That means that Alice's account will be the one used to perform runtime upgrades throughout this tutorial.

Runtime Upgrade Resource Accounting

Dispatchable calls in Substrate are always associated with a weight, which is used for resource accounting. FRAME's System module bounds extrinsics to a block length and weight limit. The set_code function in the System module is intentionally designed to consume the maximum weight that may fit in a block. The set_code function's weight annotation also specifies that set_code is in the Operational class of dispatchable functions, which identifies it as relating to network operations and impacts the accounting of its resources, such as by exempting it from the TransactionByteFee. In order to work within FRAME's safeguards around resource accounting, the Sudo pallet provides the sudo_unchecked_weight function, which provides the same capability as the sudo function, but accepts an additional parameter that is used to specify the (possibly zero) weight to use for the call. The sudo_unchecked_weight function is what will be used to invoke the runtime upgrade in this section of this tutorial; in the next section, the Scheduler pallet will be used to manage the resources consumed by the set_code function.

Prepare an Upgraded Runtime

Because the template node doesn't come with the Scheduler pallet included in its runtime, the first runtime upgrade performed in this tutorial will add that pallet. First, add the Scheduler pallet as a dependency in the template node's runtime/Cargo.toml file.

pallet-scheduler = { default-features = false, version = '2.0.0' }

#--snip--

[features]
default = ['std']
std = [
    #--snip--
    'pallet-scheduler/std',
    #--snip--
]

Next, add the pallet to the runtime by updating runtime/src/lib.rs.

// Define the types required by the Scheduler pallet.
parameter_types! {
    pub MaximumSchedulerWeight: Weight = Perbill::from_percent(80) * MaximumBlockWeight::get();
    pub const MaxScheduledPerBlock: u32 = 50;
}

// Configure the runtime's implementation of the Scheduler pallet.
impl pallet_scheduler::Trait for Runtime {
    type Event = Event;
    type Origin = Origin;
    type PalletsOrigin = OriginCaller;
    type Call = Call;
    type MaximumWeight = MaximumSchedulerWeight;
    type ScheduleOrigin = frame_system::EnsureRoot<AccountId>;
    type MaxScheduledPerBlock = MaxScheduledPerBlock;
    type WeightInfo = ();
}

// Add the Scheduler pallet inside the construct_runtime! macro.
construct_runtime!(
    pub enum Runtime where
        Block = Block,
        NodeBlock = opaque::Block,
        UncheckedExtrinsic = UncheckedExtrinsic
    {
        /*** snip ***/
        Scheduler: pallet_scheduler::{Module, Call, Storage, Event<T>},
    }
);

The final step to preparing an upgraded FRAME runtime is to increment its spec_version, which is a member of the RuntimeVersion struct that is defined in runtime/src/lib.rs.

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,  // Update this value.
    impl_version: 1,
    apis: RUNTIME_API_VERSIONS,
    transaction_version: 1,
};

Take a moment to review the components of the RuntimeVersion struct:

  • spec_name: The name of the runtime/chain, e.g. Ethereum.
  • impl_name: The name of the client, e.g. OpenEthereum.
  • authoring_version: The authorship version for block authors.
  • spec_version: The version of the runtime/chain.
  • impl_version: The version of the client.
  • apis: The list of supported APIs.
  • transaction_version: The version of the dispatchable function interface.

In order to upgrade the runtime it is required to increase the spec_version; refer to the implementation of the FRAME System module and in particular the can_set_code function to to see how this requirement and others are enforced by runtime logic.

Build the upgraded runtime.

cargo build --release -p node-template-runtime

Notice that the --release flag has been added to the cargo build command; although this will result in a longer compile time, it will generate a smaller build artifact that is better suited for submitting to the blockchain network. Aside from this use case, the --release flag should be used any time you are preparing a binary for use in production, as it will result in an optimized executable application. When the --release flag is specified, build artifacts are output to the target/release directory; when the flag is omitted they will be sent to target/debug. Refer to the official documentation to learn more about building Rust code with Cargo.

Upgrade the Runtime

Use this link to open the Polkadot JS Apps UI and automatically configure the UI to connect to the local node: https://polkadot.js.org/apps/#/extrinsics?rpc=ws://127.0.0.1:9944. Use Alice's account to invoke the sudoUncheckedWeight function and use the setCode function from the system pallet as its parameter. In order to supply the build artifact that was generated by the previous build step, toggle the "file upload" switch on the right-hand side of the "code" input field for the parameter to the setCode function. Click the "code" input field, and select the Wasm binary that defines the upgraded runtime: target/release/wbuild/node-template-runtime/node_template_runtime.compact.wasm. Leave the value for the _weight parameter at the default of 0. Click "Submit Transaction" and then "Sign and Submit".

Sudo Upgrade

After the transaction has been included in a block, the version number in the upper-left-hand corner of Polkadot JS Apps UI should reflect that the runtime version is now 2.

Version 2

Last updated on 2/24/2021 by Dan Forbes
← IntroductionSchedule an Upgrade →
  • Start the Template Node
  • Runtime Upgrade Resource Accounting
  • Prepare an Upgraded Runtime
  • Upgrade the Runtime
Substrate Developer Hub
Developer Hub
TutorialsKnowledge BaseRecipesAPI Reference
Community
Community HomeNewsletterSubstrate Technical ChatSubstrate SeminarStack OverflowTwitterEvents
More
Substrate Builders ProgramBlogSubstrate GitHubDeveloper Hub GitHubPrivacy PolicyTerms of UseCookie Settings
Copyright © 2021 Parity Technologies