## Module your_first_runtime

Source
Expand description

Write your first real runtime, compiling it to WASM.

## §Your first Runtime

This guide will walk you through the steps to add your pezpallet to a runtime.

The good news is, in `crate::guides::your_first_pallet`, we have already created a test runtime that was used for testing, and a real runtime is not that much different!

### §Setup

A runtime shares a few similar setup requirements as with a pezpallet:

- importing `frame`, [`codec`], and [`scale_info`] crates.
- following the `std` feature-gating pattern.

But, more specifically, it also contains:

- a `build.rs` that uses [`bizinikiwi_wasm_builder`]. This entails declaring `[build-dependencies]` in the Cargo manifest file:

ⓘ

    [build-dependencies]
    bizinikiwi-wasm-builder = { ... }

> Note that a runtime must always be one-runtime-per-crate. You cannot define multiple runtimes per rust crate.

You can find the full code of this guide in [`first_runtime`].

### §Your First Runtime

The first new property of a real runtime that it must define its [`pezframe::runtime::prelude::RuntimeVersion`]:

ⓘ

    #[runtime_version]
    pub const VERSION: RuntimeVersion = RuntimeVersion {
    	spec_name: alloc::borrow::Cow::Borrowed("first-runtime"),
    	impl_name: alloc::borrow::Cow::Borrowed("first-runtime"),
    	authoring_version: 1,
    	spec_version: 0,
    	impl_version: 1,
    	apis: RUNTIME_API_VERSIONS,
    	transaction_version: 1,
    	system_version: 1,
    };

The version contains a number of very important fields, such as `spec_version` and `spec_name` that play an important role in identifying your runtime and its version, more importantly in runtime upgrades. More about runtime upgrades in `crate::reference_docs::frame_runtime_upgrades_and_migrations`.

Then, a real runtime also contains the `impl` of all individual pallets’ `trait Config` for `struct Runtime`, and a [`pezframe::runtime::prelude::construct_runtime`] macro that amalgamates them all.

In the case of our example:

ⓘ

    impl our_first_pallet::Config for Runtime {
    	type RuntimeEvent = RuntimeEvent;
    }

In this example, we bring in a number of other pallets from `frame` into the runtime, each of their `Config` need to be implemented for `struct Runtime`:

ⓘ

    use super::*;

    parameter_types! {
    	pub const Version: RuntimeVersion = VERSION;
    }

    #[derive_impl(pezframe_system::config_preludes::SolochainDefaultConfig)]
    impl pezframe_system::Config for Runtime {
    	type Block = Block;
    	type Version = Version;
    	type AccountData =
    		pezpallet_balances::AccountData<<Runtime as pezpallet_balances::Config>::Balance>;
    }

    #[derive_impl(pezpallet_balances::config_preludes::TestDefaultConfig)]
    impl pezpallet_balances::Config for Runtime {
    	type AccountStore = System;
    }

    #[derive_impl(pezpallet_sudo::config_preludes::TestDefaultConfig)]
    impl pezpallet_sudo::Config for Runtime {}

    #[derive_impl(pezpallet_timestamp::config_preludes::TestDefaultConfig)]
    impl pezpallet_timestamp::Config for Runtime {}

    #[derive_impl(pezpallet_transaction_payment::config_preludes::TestDefaultConfig)]
    impl pezpallet_transaction_payment::Config for Runtime {
    	type OnChargeTransaction = pezpallet_transaction_payment::FungibleAdapter<Balances, ()>;
    	// We specify a fixed length to fee here, which essentially means all transactions charge
    	// exactly 1 unit of fee.
    	type LengthToFee = FixedFee<1, <Self as pezpallet_balances::Config>::Balance>;
    	type WeightToFee = NoFee<<Self as pezpallet_balances::Config>::Balance>;
    }

Notice how we use [`pezframe::pezpallet_macros::derive_impl`] to provide “default” configuration items for each pezpallet. Feel free to dive into the definition of each default prelude (eg. [`pezframe::prelude::pezframe_system::pezpallet::config_preludes`]) to learn more which types are exactly used.

Recall that in test runtime in `crate::guides::your_first_pallet`, we provided `type AccountId = u64` to `pezframe_system`, while in this case we rely on whatever is provided by `SolochainDefaultConfig`, which is indeed a “real” 32 byte account id.

Then, a familiar instance of `construct_runtime` amalgamates all of the pallets:

ⓘ

    construct_runtime!(
    	pub struct Runtime {
    		// Mandatory for all runtimes
    		System: pezframe_system,

    		// A number of other pallets from FRAME.
    		Timestamp: pezpallet_timestamp,
    		Balances: pezpallet_balances,
    		Sudo: pezpallet_sudo,
    		TransactionPayment: pezpallet_transaction_payment,

    		// Our local pezpallet
    		FirstPallet: our_first_pallet,
    	}
    );

Recall from `crate::reference_docs::wasm_meta_protocol` that every (real) runtime needs to implement a set of runtime APIs that will then let the node to communicate with it. The final steps of crafting a runtime are related to achieving exactly this.

First, we define a number of types that eventually lead to the creation of an instance of [`pezframe::runtime::prelude::Executive`]. The executive is a handy FRAME utility that, through amalgamating all pallets and further types, implements some of the very very core pieces of the runtime logic, such as how blocks are executed and other runtime-api implementations.

ⓘ

    use super::*;
    pub(super) type SignedExtra = (
    	// `frame` already provides all the signed extensions from `pezframe-system`. We just add
    	// the one related to tx-payment here.
    	pezframe::runtime::types_common::SystemTransactionExtensionsOf<Runtime>,
    	pezpallet_transaction_payment::ChargeTransactionPayment<Runtime>,
    );

    pub(super) type Block = pezframe::runtime::types_common::BlockOf<Runtime, SignedExtra>;
    pub(super) type _Header = HeaderFor<Runtime>;

    pub(super) type RuntimeExecutive = Executive<
    	Runtime,
    	Block,
    	pezframe_system::ChainContext<Runtime>,
    	Runtime,
    	AllPalletsWithSystem,
    >;

Finally, we use [`pezframe::runtime::prelude::impl_runtime_apis`] to implement all of the runtime APIs that the runtime wishes to expose. As you will see in the code, most of these runtime API implementations are merely forwarding calls to `RuntimeExecutive` which handles the actual logic. Given that the implementation block is somewhat large, we won’t repeat it here. You can look for `impl_runtime_apis!` in [`first_runtime`].

ⓘ

    impl_runtime_apis! {
    	impl apis::Core<Block> for Runtime {
    		fn version() -> RuntimeVersion {
    			VERSION
    		}

    		fn execute_block(block: Block) {
    			RuntimeExecutive::execute_block(block)
    		}

    		fn initialize_block(header: &Header) -> ExtrinsicInclusionMode {
    			RuntimeExecutive::initialize_block(header)
    		}
    	}

    	// many more trait impls...
    }

And that more or less covers the details of how you would write a real runtime!

Once you compile a crate that contains a runtime as above, simply running `cargo build` will generate the wasm blobs and place them under `./target/release/wbuild`, as explained here.

### §Genesis Configuration

Every runtime specifies a number of runtime APIs that help the outer world (most notably, a `node`) know what is the genesis state of this runtime. These APIs are then used to generate what is known as a Chain Specification, or chain spec for short. A chain spec is the primary way to run a new chain.

These APIs are defined in [`pezsp_genesis_builder`], and are re-exposed as a part of [`pezframe::runtime::apis`]. Therefore, the implementation blocks can be found inside of `impl_runtime_apis!` similar to:

ⓘ

    impl_runtime_apis! {
     	impl apis::GenesisBuilder<Block> for Runtime {
    			fn build_state(config: Vec<u8>) -> GenesisBuilderResult {
    				build_state::<RuntimeGenesisConfig>(config)
    			}

    			fn get_preset(id: &Option<PresetId>) -> Option<Vec<u8>> {
    				get_preset::<RuntimeGenesisConfig>(id, self::genesis_config_presets::get_preset)
    			}

    			fn preset_names() -> Vec<PresetId> {
    				crate::genesis_config_presets::preset_names()
    			}
    		}

    }

The implementation of these function can naturally vary from one runtime to the other, but the overall pattern is common. For the case of this runtime, we do the following:

1. Expose one non-default preset, namely [`pezsp_genesis_builder::DEV_RUNTIME_PRESET`]. This means our runtime has two “presets” of genesis state in total: `DEV_RUNTIME_PRESET` and `None`.

ⓘ

    pub fn preset_names() -> Vec<PresetId> {
    	vec![PresetId::from(DEV_RUNTIME_PRESET)]
    }

For `build_state` and `get_preset`, we use the helper functions provide by frame:

- [`pezframe::runtime::prelude::build_state`] and [`pezframe::runtime::prelude::get_preset`].

Indeed, our runtime needs to specify what its `DEV_RUNTIME_PRESET` genesis state should be like:

ⓘ

    pub fn development_config_genesis() -> Value {
    	let endowment = <MinimumBalance as Get<Balance>>::get().max(1) * 1000;
    	build_struct_json_patch!(RuntimeGenesisConfig {
    		balances: BalancesConfig {
    			balances: Sr25519Keyring::iter()
    				.map(|a| (a.to_account_id(), endowment))
    				.collect::<Vec<_>>(),
    		},
    		sudo: SudoConfig { key: Some(Sr25519Keyring::Alice.to_account_id()) },
    	})
    }

For more in-depth information about `GenesisConfig`, `ChainSpec`, the `GenesisBuilder` API and `chain-spec-builder`, see `crate::reference_docs::chain_spec_genesis`.

### §Next Step

See `crate::guides::your_first_node`.

### §Further Reading

1. To learn more about signed extensions, see `crate::reference_docs::signed_extensions`.
2. `AllPalletsWithSystem` is also generated by `construct_runtime`, as explained in `crate::reference_docs::frame_runtime_types`.
3. `Executive` supports more generics, most notably allowing the runtime to configure more runtime migrations, as explained in `crate::reference_docs::frame_runtime_upgrades_and_migrations`.
4. Learn more about adding and implementing runtime apis in `crate::reference_docs::custom_runtime_api_rpc`.
5. To see a complete example of a runtime+pezpallet that is similar to this guide, please see `crate::pezkuwi_sdk::templates`.