Skip to Content
Tokio Upgrade Docs are released! 🎉
Tokio V3Runtime Upgrade System - Technical Documentation

Runtime Upgrade System - Technical Documentation

Overview

The Splendor blockchain implements a sophisticated runtime upgrade system that enables protocol evolution without network disruption. This documentation provides technical details and visual representations of the upgrade process.


How Forkless Runtime Upgrades Work (Substrate & Polkadot)

One of the most powerful features of Substrate-based blockchains (like Splendor and Polkadot) is the ability to upgrade the runtime without requiring a hard fork. This is called a “forkless runtime upgrade” and is achieved by storing the blockchain’s logic as WebAssembly (Wasm) code on-chain. When an upgrade is approved (usually by on-chain governance), the new Wasm code is swapped in, and all nodes begin running the new logic automatically.

Key points:

  • Upgrades are trustless and decentralized—no need for node operators to manually update software.
  • The upgrade process is governed by on-chain proposals and voting, ensuring transparency and community involvement.
  • Both solo chains and parachains can upgrade, but parachains require relay chain approval.

Runtime Versioning

Each runtime has a version number (spec_version, impl_version, transaction_version) that helps nodes determine compatibility and when to switch to the new code. The runtime version can be queried via RPC (e.g., state_getRuntimeVersion).

Storage Migrations

When an upgrade changes how data is stored, a one-time migration function is run to update the storage format. This is implemented using the OnRuntimeUpgrade trait in FRAME. Migrations are ordered and run before any new blocks are produced with the new logic.

Best Practices for Safe Upgrades

  • Pre-checks: Use tools like try-runtime to simulate upgrades and catch errors before deploying on mainnet.
  • CI Integration: Automate checks for storage migrations and feature compatibility in your CI pipeline.
  • Governance: Propose upgrades through on-chain governance, allowing the community to review and vote.
  • Monitoring: After an upgrade, monitor node logs and chain metrics for any unexpected behavior.

Parachain-Specific Notes

  • Parachains must use authorize_upgrade and apply_authorized_upgrade to coordinate with the relay chain.
  • Storage migrations and runtime versioning work the same, but require extra approval steps.

Resources:


System Architecture

Storage Schema Evolution

Code Implementation

1. Storage Schema Definition

pub enum EthereumStorageSchema { V1, V2, V3, }

2. Runtime Upgrade Handler

impl frame_support::traits::OnRuntimeUpgrade for OnRuntimeUpgrade { fn on_runtime_upgrade() -> frame_support::weights::Weight { // Update storage schema frame_support::storage::unhashed::put::<EthereumStorageSchema>( PALLET_ETHEREUM_SCHEMA, &EthereumStorageSchema::V3, ); // Update validator set <pallet_validator_admin::Validators<Runtime>>::put(vec![ // Validator configuration ]); // Return weight consumed <Runtime as frame_system::Config>::DbWeight::get().writes(1) } }

3. Versioned API Implementation

#[api_version(5)] pub trait EthereumRuntimeRPCApi { fn chain_id() -> u64; fn account_basic(address: Address) -> fp_evm::Account; fn gas_price() -> U256; // ... other API methods }

Upgrade Flow

Storage Migration Process

Code Examples

1. Pre-upgrade Validation

#[cfg(feature = "try-runtime")] pub fn pre_migrate_block_v2() -> Result<Vec<u8>, &'static str> { let item = b"CurrentBlock"; let block_v0 = frame_support::storage::migration::get_storage_value::<ethereum::BlockV0>( Self::name().as_bytes(), item, &[], ); // Validation logic }

2. Post-upgrade Verification

#[cfg(feature = "try-runtime")] pub fn post_migrate_block_v2(v0_data: Vec<u8>) -> Result<(), &'static str> { let (v0_number, v0_parent_hash, v0_transaction_len) = Decode::decode( &mut v0_data.as_slice(), )?; // Verification logic }

3. EIP-1559 Implementation

fn is_eip1559(&self, block_hash: B::Hash) -> bool { let api = self.client.runtime_api(); if let Some(api_version) = Self::api_version(&api, block_hash) { api_version >= 2 } else { false } }

Storage Structure

Upgrade Components