fix: Resolve cargo clippy errors and add CI workflow plan

## Changes

### Clippy Fixes
- Fixed deprecated `cargo_bin` usage in 27 test files (added #![allow(deprecated)])
- Fixed uninlined_format_args in zombienet-sdk-tests
- Fixed subxt API changes in revive/rpc/tests.rs (fetch signature, StorageValue)
- Fixed dead_code warnings in validator-pool and identity-kyc mocks
- Fixed field name `i` -> `_i` in tasks example

### CI Infrastructure
- Added .claude/WORKFLOW_PLAN.md for tracking CI fix progress
- Updated lychee.toml and taplo.toml configs

### Files Modified
- 27 test files with deprecated cargo_bin fix
- bizinikiwi/pezframe/revive/rpc/src/tests.rs (subxt API)
- pezkuwi/pezpallets/validator-pool/src/{mock,tests}.rs
- pezcumulus/teyrchains/pezpallets/identity-kyc/src/mock.rs
- bizinikiwi/pezframe/examples/tasks/src/tests.rs

## Status
- cargo clippy: PASSING
- Next: cargo fmt, zepter, workspace checks
This commit is contained in:
2025-12-22 16:36:14 +03:00
parent a0f04820ab
commit 3208f208c0
1387 changed files with 16564 additions and 178629 deletions
@@ -17,22 +17,24 @@
//! - patch
//! - raw
//!
//! Each of the formats is explained in [_chain-spec-format_][`pezsc_chain_spec#chain-spec-formats`].
//! Each of the formats is explained in
//! [_chain-spec-format_][`pezsc_chain_spec#chain-spec-formats`].
//!
//!
//! # `GenesisConfig` for `pezpallet`
//!
//! Every frame pezpallet may have its initial state which is defined by the `GenesisConfig` internal
//! struct. It is a regular Rust struct, annotated with the [`pezpallet::genesis_config`] attribute.
//! Every frame pezpallet may have its initial state which is defined by the `GenesisConfig`
//! internal struct. It is a regular Rust struct, annotated with the [`pezpallet::genesis_config`]
//! attribute.
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/pallets.rs", pezpallet_bar_GenesisConfig)]
//!
//! The struct shall be defined within the pezpallet `mod`, as in the following code:
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/pallets.rs", pezpallet_bar)]
//!
//! The initial state conveyed in the `GenesisConfig` struct is transformed into state storage
//! items by means of the [`BuildGenesisConfig`] trait, which shall be implemented for the pezpallet's
//! `GenesisConfig` struct. The [`pezpallet::genesis_build`] attribute shall be attached to the `impl`
//! block:
//! items by means of the [`BuildGenesisConfig`] trait, which shall be implemented for the
//! pezpallet's `GenesisConfig` struct. The [`pezpallet::genesis_build`] attribute shall be attached
//! to the `impl` block:
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/pallets.rs", pezpallet_bar_build)]
//!
//! `GenesisConfig` may also contain more complicated types, including nested structs or enums, as
@@ -51,11 +53,11 @@
//!
//! # `GenesisConfig` for `runtimes`
//!
//! The runtime genesis config struct consists of configs for every pezpallet. For the [_demonstration
//! runtime_][`pez_chain_spec_guide_runtime`] used in this guide, it consists of `SystemConfig`,
//! `BarConfig`, and `FooConfig`. This structure was automatically generated by a macro and it can
//! be sneak-peeked here: [`RuntimeGenesisConfig`]. For further reading on generated runtime
//! types, refer to [`frame_runtime_types`].
//! The runtime genesis config struct consists of configs for every pezpallet. For the
//! [_demonstration runtime_][`pez_chain_spec_guide_runtime`] used in this guide, it consists of
//! `SystemConfig`, `BarConfig`, and `FooConfig`. This structure was automatically generated by a
//! macro and it can be sneak-peeked here: [`RuntimeGenesisConfig`]. For further reading on
//! generated runtime types, refer to [`frame_runtime_types`].
//!
//! The macro automatically adds an attribute that renames all the fields to [`camelCase`]. It is a
//! good practice to add it to nested structures too, to have the naming of the JSON keys consistent
@@ -159,8 +161,8 @@
//!
//! The [`chain_spec_builder`] util allows interaction with the runtime in order to list or display
//! presets and build the chain specification file. It is possible to use the tool with the
//! [_demonstration runtime_][`pez_chain_spec_guide_runtime`]. To build the required packages, just run
//! the following command:
//! [_demonstration runtime_][`pez_chain_spec_guide_runtime`]. To build the required packages, just
//! run the following command:
//!
//! ```ignore
//! cargo build -p pezstaging-chain-spec-builder -p pez-chain-spec-guide-runtime --release
@@ -38,28 +38,28 @@ bizinikiwi-wasm-builder = { optional = true, workspace = true, default-features
[features]
default = ["std"]
std = [
"codec/std",
"scale-info/std",
"codec/std",
"scale-info/std",
"pezframe-support/std",
"frame/std",
"frame/std",
"pezframe-support/std",
"pezsp-application-crypto/std",
"pezsp-core/std",
"pezsp-genesis-builder/std",
"pezsp-keyring/std",
"pezsp-runtime/std",
"pezsp-application-crypto/std",
"pezsp-core/std",
"pezsp-genesis-builder/std",
"pezsp-keyring/std",
"pezsp-runtime/std",
"serde/std",
"serde_json/std",
"bizinikiwi-wasm-builder",
"bizinikiwi-wasm-builder",
"serde/std",
"serde_json/std",
]
runtime-benchmarks = [
"pezframe-support/runtime-benchmarks",
"frame/runtime-benchmarks",
"pezsc-chain-spec/runtime-benchmarks",
"pezsp-genesis-builder/runtime-benchmarks",
"pezsp-keyring/runtime-benchmarks",
"pezsp-runtime/runtime-benchmarks",
"bizinikiwi-wasm-builder?/runtime-benchmarks",
"bizinikiwi-wasm-builder?/runtime-benchmarks",
"frame/runtime-benchmarks",
"pezframe-support/runtime-benchmarks",
"pezsc-chain-spec/runtime-benchmarks",
"pezsp-genesis-builder/runtime-benchmarks",
"pezsp-keyring/runtime-benchmarks",
"pezsp-runtime/runtime-benchmarks",
]
@@ -7,8 +7,8 @@
//!
//! To declare a set of functions as host functions, you need to use the `#[runtime_interface]`
//! ([`pezsp_runtime_interface`]) attribute macro. The most notable set of host functions are those
//! that allow the runtime to access the chain state, namely [`pezsp_io::storage`]. Some other notable
//! host functions are also defined in [`pezsp_io`].
//! that allow the runtime to access the chain state, namely [`pezsp_io::storage`]. Some other
//! notable host functions are also defined in [`pezsp_io`].
//!
//! ## Adding New Host Functions
//!
@@ -22,6 +22,6 @@
//! A group of host functions can always be grouped to gether as a tuple:
#![doc = docify::embed!("../../bizinikiwi/primitives/io/src/lib.rs", BizinikiwiHostFunctions)]
//!
//! The host functions are attached to the node side's [`pezsc_executor::WasmExecutor`]. For example in
//! the minimal template, the setup looks as follows:
//! The host functions are attached to the node side's [`pezsc_executor::WasmExecutor`]. For example
//! in the minimal template, the setup looks as follows:
#![doc = docify::embed!("../../templates/minimal/node/src/service.rs", FullClient)]
@@ -124,7 +124,8 @@
//! A developer should use fixed-point instead of floating-point arithmetic to mitigate the
//! potential for inaccuracy, rounding errors, or other unexpected behavior.
//!
//! - [Fixed point types](pezsp_arithmetic::fixed_point) and their associated usage can be found here.
//! - [Fixed point types](pezsp_arithmetic::fixed_point) and their associated usage can be found
//! here.
//! - [PerThing](pezsp_arithmetic::per_things) and its associated types can be found here.
//!
//! Using floating point number types (i.e. f32, f64) in the runtime should be avoided, as a single non-deterministic result could cause chaos for blockchain consensus along with the issues above. For more on the specifics of the peculiarities of floating point calculations, [watch this video by the Computerphile](https://www.youtube.com/watch?v=PZRI1IfStY0).
@@ -214,9 +215,9 @@
//!
//! #### Bob's Overflowed Balance
//!
//! **Bob's** balance exceeds the `Balance` type on the `EduChain`. Because the pezpallet developer did
//! not handle the calculation to add to Bob's balance with any regard to this overflow, **Bob's**
//! balance is now essentially `0`, the operation **wrapped**.
//! **Bob's** balance exceeds the `Balance` type on the `EduChain`. Because the pezpallet developer
//! did not handle the calculation to add to Bob's balance with any regard to this overflow,
//! **Bob's** balance is now essentially `0`, the operation **wrapped**.
//!
//! <details>
//! <summary><b>Solution: Saturating or Checked</b></summary>
@@ -276,8 +277,8 @@
//! authoring, consensus, or other protocol-level dependencies, going through with an action may
//! actually cause harm to the network, and thus stalling would be the better option.
//!
//! Take the example of the BABE pezpallet ([`pezpallet_babe`]), which doesn't allow for a validator to
//! participate if it is disabled (see: [`frame::traits::DisabledValidators`]):
//! Take the example of the BABE pezpallet ([`pezpallet_babe`]), which doesn't allow for a validator
//! to participate if it is disabled (see: [`frame::traits::DisabledValidators`]):
//!
//! ```ignore
//! if T::DisabledValidators::is_disabled(authority_index) {
@@ -94,16 +94,17 @@
//! address is the first generic parameter of [`pezsp_runtime::generic::UncheckedExtrinsic`], and so
//! can vary from chain to chain.
//!
//! The address type used on the Pezkuwi relay chain is [`pezsp_runtime::MultiAddress<AccountId32>`],
//! where `AccountId32` is defined [here][`pezsp_core::crypto::AccountId32`]. When constructing a
//! signed extrinsic to be submitted to a Pezkuwi node, you'll always use the
//! [`pezsp_runtime::MultiAddress::Id`] variant to wrap your `AccountId32`.
//! The address type used on the Pezkuwi relay chain is
//! [`pezsp_runtime::MultiAddress<AccountId32>`], where `AccountId32` is defined
//! [here][`pezsp_core::crypto::AccountId32`]. When constructing a signed extrinsic to be submitted
//! to a Pezkuwi node, you'll always use the [`pezsp_runtime::MultiAddress::Id`] variant to wrap
//! your `AccountId32`.
//!
//! #### signature
//!
//! This is the [SCALE encoded][frame::deps::codec] signature. The signature type is configured via
//! the third generic parameter of [`pezsp_runtime::generic::UncheckedExtrinsic`], which determines the
//! shape of the signature and signing algorithm that should be used.
//! the third generic parameter of [`pezsp_runtime::generic::UncheckedExtrinsic`], which determines
//! the shape of the signature and signing algorithm that should be used.
//!
//! The signature is obtained by signing the _signed payload_ bytes (see below on how this is
//! constructed) using the private key associated with the address and correct algorithm.
@@ -123,9 +124,9 @@
//! This is the concatenation of the [SCALE encoded][frame::deps::codec] bytes representing first a
//! single byte describing the extension version (this is bumped whenever a change occurs in the
//! transaction extension pipeline) followed by the bytes of each of the [_transaction
//! extensions_][pezsp_runtime::traits::TransactionExtension], and are configured by the fourth generic
//! parameter of [`pezsp_runtime::generic::UncheckedExtrinsic`]. Learn more about transaction
//! extensions [here][crate::reference_docs::transaction_extensions].
//! extensions_][pezsp_runtime::traits::TransactionExtension], and are configured by the fourth
//! generic parameter of [`pezsp_runtime::generic::UncheckedExtrinsic`]. Learn more about
//! transaction extensions [here][crate::reference_docs::transaction_extensions].
//!
//! When it comes to constructing an extrinsic, each transaction extension has two things that we
//! are interested in here:
@@ -155,8 +156,8 @@
//!
//! A call can be anything that implements [`Encode`][frame::deps::codec::Encode]. In FRAME-based
//! runtimes, a call is represented as an enum of enums, where the outer enum represents the FRAME
//! pezpallet being called, and the inner enum represents the call being made within that pezpallet, and
//! any arguments to it. Read more about the call enum
//! pezpallet being called, and the inner enum represents the call being made within that pezpallet,
//! and any arguments to it. Read more about the call enum
//! [here][crate::reference_docs::frame_runtime_types].
//!
//! FRAME `Call` enums are automatically generated, and end up looking something like this:
@@ -172,16 +173,16 @@
//! )
//! ```
//!
//! - `pezpallet_index` is a single byte denoting the index of the pezpallet that we are calling into, and
//! is what the tag of the outermost enum will encode to.
//! - `pezpallet_index` is a single byte denoting the index of the pezpallet that we are calling
//! into, and is what the tag of the outermost enum will encode to.
//! - `call_index` is a single byte denoting the index of the call that we are making the pezpallet,
//! and is what the tag of the inner enum will encode to.
//! - `call_args` are the SCALE encoded bytes for each of the arguments that the call expects, and
//! are typically provided as values to the inner enum.
//!
//! Information about the pallets that exist for a chain (including their indexes), the calls
//! available in each pezpallet (including their indexes), and the arguments required for each call can
//! be found in the metadata for the chain. For V15 metadata, this information [is
//! available in each pezpallet (including their indexes), and the arguments required for each call
//! can be found in the metadata for the chain. For V15 metadata, this information [is
//! here][frame::deps::pezframe_support::__private::metadata::v15::PalletMetadata].
//!
//! # The Signed Payload Format
@@ -207,8 +208,8 @@
//!
//! Once we've concatenated those together, we hash the result using a Blake2 256bit hasher.
//!
//! The [`pezsp_runtime::generic::SignedPayload`] type takes care of assembling the correct payload for
//! us, given `call_data` and a tuple of transaction extensions.
//! The [`pezsp_runtime::generic::SignedPayload`] type takes care of assembling the correct payload
//! for us, given `call_data` and a tuple of transaction extensions.
//!
//! # The General Transaction Format
//!
@@ -224,8 +225,8 @@
//!
//! # Example Encoding
//!
//! Using [`pezsp_runtime::generic::UncheckedExtrinsic`], we can construct and encode an extrinsic as
//! follows:
//! Using [`pezsp_runtime::generic::UncheckedExtrinsic`], we can construct and encode an extrinsic
//! as follows:
#![doc = docify::embed!("./src/reference_docs/extrinsic_encoding.rs", encoding_example)]
#[docify::export]
+6 -6
View File
@@ -18,8 +18,8 @@
//! ```
//!
//! within the pezpallet, if you want to use the standard `println!`, it needs to be wrapped in
//! [`pezsp_std::if_std`]. Of course, this means that this print code is only available to you in the
//! `std` compiler flag, and never present in a wasm build.
//! [`pezsp_std::if_std`]. Of course, this means that this print code is only available to you in
//! the `std` compiler flag, and never present in a wasm build.
//!
//! ```
//! // somewhere in your pezpallet. This is not a real pezpallet code.
@@ -59,8 +59,8 @@
//!
//! More conveniently, the `frame` umbrella crate re-exports the log crate as [`frame::log`].
//!
//! Then, the pezpallet can use this crate to emit log statements. In this statement, we use the info
//! level, and the target is `pezpallet-example`.
//! Then, the pezpallet can use this crate to emit log statements. In this statement, we use the
//! info level, and the target is `pezpallet-example`.
//!
//! ```
//! mod pezpallet {
@@ -115,8 +115,8 @@
//!
//! Under the hood, logging is another instance of host functions under the hood (as defined in
//! [`crate::reference_docs::wasm_meta_protocol`]). The runtime uses a set of host functions under
//! [`pezsp_io::logging`] and [`pezsp_io::misc`] to emit all logs and prints. You typically do not need to
//! use these APIs directly.
//! [`pezsp_io::logging`] and [`pezsp_io::misc`] to emit all logs and prints. You typically do not
//! need to use these APIs directly.
//!
//! ## Using Logging in Production
//!
@@ -11,8 +11,8 @@
//!
//! Recall from [`crate::reference_docs::wasm_meta_protocol`] that the node and the runtime
//! communicate with one another via host functions and runtime APIs. Many of these interactions
//! contribute to the actual state transition of the blockchain. For example [`pezsp_api::Core`] is the
//! main runtime API that is called to execute new blocks.
//! contribute to the actual state transition of the blockchain. For example [`pezsp_api::Core`] is
//! the main runtime API that is called to execute new blocks.
//!
//! Offchain workers are in principle not different in any way: It is a runtime API exposed by the
//! wasm blob ([`pezsp_offchain::OffchainWorkerApi`]), and the node software calls into it when it
@@ -77,8 +77,8 @@
//! }
//! ```
//!
//! Additionally, [`pezsp_runtime::offchain`] provides a set of utilities that can be used to moderate
//! the execution of offchain workers.
//! Additionally, [`pezsp_runtime::offchain`] provides a set of utilities that can be used to
//! moderate the execution of offchain workers.
//!
//! ## Think Twice: Why Use Bizinikiwi's Offchain Workers?
//!
+11 -10
View File
@@ -6,8 +6,8 @@
//!
//! FRAME's origin abstractions allow you to convey meanings far beyond just an account-id being the
//! caller of an extrinsic. Nonetheless, an account-id having signed an extrinsic is one of the
//! meanings that an origin can convey. This is the commonly used [`pezframe_system::ensure_signed`],
//! where the return value happens to be an account-id.
//! meanings that an origin can convey. This is the commonly used
//! [`pezframe_system::ensure_signed`], where the return value happens to be an account-id.
//!
//! Instead, let's establish the following as the correct definition of an origin:
//!
@@ -44,8 +44,8 @@
//! amalgamated at the runtime level. Read [`crate::reference_docs::frame_runtime_types`] to
//! familiarize yourself with these types.
//!
//! To understand this better, we will next create a pezpallet with a custom origin, which will add a
//! new variant to `RuntimeOrigin`.
//! To understand this better, we will next create a pezpallet with a custom origin, which will add
//! a new variant to `RuntimeOrigin`.
//!
//! ## Adding Custom Pezpallet Origin to the Runtime
//!
@@ -67,9 +67,9 @@
//!
//! ## Asserting on a Custom Internal Origin
//!
//! In order to assert on a custom origin that is defined within your pezpallet, we need a way to first
//! convert the `<T as pezframe_system::Config>::RuntimeOrigin` into the local `enum Origin` of the
//! current pezpallet. This is a common process that is explained in
//! In order to assert on a custom origin that is defined within your pezpallet, we need a way to
//! first convert the `<T as pezframe_system::Config>::RuntimeOrigin` into the local `enum Origin`
//! of the current pezpallet. This is a common process that is explained in
//! [`crate::reference_docs::frame_runtime_types#
//! adding-further-constraints-to-runtime-composite-enums`].
//!
@@ -103,8 +103,8 @@
//! to us, and are defined in other pallets.
//!
//! For example, [`pezpallet_collective`] defines [`pezpallet_collective::EnsureMember`] and
//! [`pezpallet_collective::EnsureProportionMoreThan`] and many more, which is exactly what we alluded
//! to earlier in this document.
//! [`pezpallet_collective::EnsureProportionMoreThan`] and many more, which is exactly what we
//! alluded to earlier in this document.
//!
//! Make sure to check the full list of [implementors of
//! `EnsureOrigin`](frame::traits::EnsureOrigin#implementors) for more inspiration.
@@ -112,7 +112,8 @@
//! ## Obtaining Abstract Origins
//!
//! So far we have learned that FRAME pallets can assert on custom and abstract origin types,
//! whether they are defined within the pezpallet or not. But how can we obtain these abstract origins?
//! whether they are defined within the pezpallet or not. But how can we obtain these abstract
//! origins?
//!
//! > All extrinsics that come from the outer world can generally only be obtained as either
//! > `signed` or `none` origin.
@@ -13,14 +13,15 @@
//!
//! FRAME pallets, as per described in [`crate::pezkuwi_sdk::frame_runtime`] are:
//!
//! > A pezpallet is a unit of encapsulated logic. It has a clearly defined responsibility and can be
//! > A pezpallet is a unit of encapsulated logic. It has a clearly defined responsibility and can
//! > be
//! linked to other pallets.
//!
//! That is to say:
//!
//! * *encapsulated*: Ideally, a FRAME pezpallet contains encapsulated logic which has clear
//! boundaries. It is generally a bad idea to build a single monolithic pezpallet that does multiple
//! things, such as handling currencies, identities and staking all at the same time.
//! boundaries. It is generally a bad idea to build a single monolithic pezpallet that does
//! multiple things, such as handling currencies, identities and staking all at the same time.
//! * *linked to other pallets*: But, adhering extensively to the above also hinders the ability to
//! write useful applications. Pallets often need to work with each other, communicate and use
//! each other's functionalities.
@@ -63,15 +64,15 @@
//!
//! ## Example
//!
//! Consider the following example, in which `pezpallet-foo` needs another pezpallet to provide the block
//! author to it, and `pezpallet-author` which has access to this information.
//! Consider the following example, in which `pezpallet-foo` needs another pezpallet to provide the
//! block author to it, and `pezpallet-author` which has access to this information.
#![doc = docify::embed!("./src/reference_docs/frame_pallet_coupling.rs", pezpallet_foo)]
#![doc = docify::embed!("./src/reference_docs/frame_pallet_coupling.rs", pezpallet_author)]
//!
//! ### Tight Coupling Pallets
//!
//! To tightly couple `pezpallet-foo` and `pezpallet-author`, we use Rust's supertrait system. When a
//! pezpallet makes its own `trait Config` be bounded by another pezpallet's `trait Config`, it is
//! To tightly couple `pezpallet-foo` and `pezpallet-author`, we use Rust's supertrait system. When
//! a pezpallet makes its own `trait Config` be bounded by another pezpallet's `trait Config`, it is
//! expressing two things:
//!
//! 1. That it can only exist in a runtime if the other pezpallet is also present.
@@ -93,12 +94,12 @@
//!
//! > We sometimes refer to such traits that help two pallets interact as "glue traits".
//!
//! Next, `pezpallet-foo` states that it needs this trait to be provided to it, at the runtime level,
//! via an associated type:
//! Next, `pezpallet-foo` states that it needs this trait to be provided to it, at the runtime
//! level, via an associated type:
#![doc = docify::embed!("./src/reference_docs/frame_pallet_coupling.rs", loose_config)]
//!
//! Then, `pezpallet-foo` can use this trait to obtain the block author, without knowing where it comes
//! from:
//! Then, `pezpallet-foo` can use this trait to obtain the block author, without knowing where it
//! comes from:
#![doc = docify::embed!("./src/reference_docs/frame_pallet_coupling.rs", loose_usage)]
//!
//! Then, if `pezpallet-author` implements this glue-trait:
@@ -120,9 +121,9 @@
//!
//! ## Frame System
//!
//! With the above information in context, we can conclude that **`pezframe_system` is a special pezpallet
//! that is tightly coupled with every other pezpallet**. This is because it provides the fundamental
//! system functionality that every pezpallet needs, such as some types like
//! With the above information in context, we can conclude that **`pezframe_system` is a special
//! pezpallet that is tightly coupled with every other pezpallet**. This is because it provides the
//! fundamental system functionality that every pezpallet needs, such as some types like
//! [`frame::prelude::pezframe_system::Config::AccountId`],
//! [`frame::prelude::pezframe_system::Config::Hash`], and some functionality such as block number,
//! etc.
@@ -132,18 +133,19 @@
//! To recap, consider the following rules of thumb:
//!
//! * In all cases, try and break down big pallets apart with clear boundaries of responsibility. In
//! general, it is easier to argue about multiple pezpallet if they only communicate together via a
//! known trait, rather than having access to all of each others public items, such as storage and
//! dispatchables.
//! general, it is easier to argue about multiple pezpallet if they only communicate together via
//! a known trait, rather than having access to all of each others public items, such as storage
//! and dispatchables.
//! * If a group of pallets is meant to work together, but is not foreseen to be generalized, or
//! used by others, consider tightly coupling pallets, *if it simplifies the development*.
//! * If a pezpallet needs a functionality provided by another pezpallet, but multiple implementations can
//! be foreseen, consider loosely coupling pallets.
//! * If a pezpallet needs a functionality provided by another pezpallet, but multiple
//! implementations can be foreseen, consider loosely coupling pallets.
//!
//! For example, all pallets in `pezkuwi-sdk` that needed to work with currencies could have been
//! tightly coupled with [`pezpallet_balances`]. But, `pezkuwi-sdk` also provides [`pezpallet_assets`]
//! (and more implementations by the community), therefore all pallets use traits to loosely couple
//! with balances or assets pezpallet. More on this in [`crate::reference_docs::frame_tokens`].
//! tightly coupled with [`pezpallet_balances`]. But, `pezkuwi-sdk` also provides
//! [`pezpallet_assets`] (and more implementations by the community), therefore all pallets use
//! traits to loosely couple with balances or assets pezpallet. More on this in
//! [`crate::reference_docs::frame_tokens`].
//!
//! ## Further References
//!
@@ -201,7 +203,8 @@ pub mod pezpallet_foo_tight {
pub struct Pezpallet<T>(_);
#[docify::export(tight_config)]
/// This pezpallet can only live in a runtime that has both `pezframe_system` and `pezpallet_author`.
/// This pezpallet can only live in a runtime that has both `pezframe_system` and
/// `pezpallet_author`.
#[pezpallet::config]
pub trait Config: pezframe_system::Config + pezpallet_author::Config {}
@@ -269,8 +272,8 @@ impl<AccountId> AuthorProvider<AccountId> for () {
pub mod runtime {
use super::*;
use pezcumulus_pezpallet_aura_ext::pezpallet;
use frame::{runtime::prelude::*, testing_prelude::*};
use pezcumulus_pezpallet_aura_ext::pezpallet;
construct_runtime!(
pub struct Runtime {
@@ -38,15 +38,15 @@
//! Let's explore how each of these affect the [`RuntimeCall`], [`RuntimeOrigin`] and
//! [`RuntimeGenesisConfig`] generated in [`runtime`] respectively.
//!
//! As observed, [`RuntimeCall`] has 3 variants, one for each pezpallet and one for `pezframe_system`. If
//! you explore further, you will soon realize that each variant is merely a pointer to the `Call`
//! type in each pezpallet, for example [`pezpallet_foo::Call`].
//! As observed, [`RuntimeCall`] has 3 variants, one for each pezpallet and one for
//! `pezframe_system`. If you explore further, you will soon realize that each variant is merely a
//! pointer to the `Call` type in each pezpallet, for example [`pezpallet_foo::Call`].
//!
//! [`RuntimeOrigin`]'s [`OriginCaller`] has two variants, one for system, and one for `pezpallet_foo`
//! which utilized [`frame::pezpallet_macros::origin`].
//! [`RuntimeOrigin`]'s [`OriginCaller`] has two variants, one for system, and one for
//! `pezpallet_foo` which utilized [`frame::pezpallet_macros::origin`].
//!
//! Finally, [`RuntimeGenesisConfig`] is composed of `pezframe_system` and a variant for `pezpallet_bar`'s
//! [`pezpallet_bar::GenesisConfig`].
//! Finally, [`RuntimeGenesisConfig`] is composed of `pezframe_system` and a variant for
//! `pezpallet_bar`'s [`pezpallet_bar::GenesisConfig`].
//!
//! You can find other composite enums by scanning [`runtime`] for other types who's name starts
//! with `Runtime`. Some of the more noteworthy ones are:
@@ -62,23 +62,23 @@
//!
//! Let's take the example of `RuntimeCall`. This is an associated type in
//! [`pezframe_system::Config::RuntimeCall`], and all pallets have access to this type, because they
//! have access to [`pezframe_system::Config`]. Finally, this type is meant to be set to outer call of
//! the entire runtime.
//! have access to [`pezframe_system::Config`]. Finally, this type is meant to be set to outer call
//! of the entire runtime.
//!
//! But, let's not forget that this is information that *we know*, and the Rust compiler does not.
//! All that the rust compiler knows about this type is *ONLY* what the trait bounds of
//! [`pezframe_system::Config::RuntimeCall`] are specifying:
#![doc = docify::embed!("../../bizinikiwi/pezframe/system/src/lib.rs", system_runtime_call)]
//!
//! So, when at a given pezpallet, one accesses `<T as pezframe_system::Config>::RuntimeCall`, the type is
//! extremely opaque from the perspective of the Rust compiler.
//! So, when at a given pezpallet, one accesses `<T as pezframe_system::Config>::RuntimeCall`, the
//! type is extremely opaque from the perspective of the Rust compiler.
//!
//! How can a pezpallet access the `RuntimeCall` type with further constraints? For example, each
//! pezpallet has its own `enum Call`, and knows that its local `Call` is a part of `RuntimeCall`,
//! therefore there should be a `impl From<Call<_>> for RuntimeCall`.
//!
//! The only way to express this using Rust's associated types is for the pezpallet to **define its own
//! associated type `RuntimeCall`, and further specify what it thinks `RuntimeCall` should be**.
//! The only way to express this using Rust's associated types is for the pezpallet to **define its
//! own associated type `RuntimeCall`, and further specify what it thinks `RuntimeCall` should be**.
//!
//! In this case, we will want to assert the existence of [`frame::traits::IsSubType`], which is
//! very similar to [`TryFrom`].
@@ -89,14 +89,15 @@
#![doc = docify::embed!("./src/reference_docs/frame_runtime_types.rs", pezpallet_with_specific_runtime_call_impl)]
//!
//! > In other words, the degree of specificity that [`pezframe_system::Config::RuntimeCall`] has is
//! > not enough for the pezpallet to work with. Therefore, the pezpallet has to define its own associated
//! > not enough for the pezpallet to work with. Therefore, the pezpallet has to define its own
//! > associated
//! > type representing `RuntimeCall`.
//!
//! Another way to look at this is:
//!
//! `pezpallet_with_specific_runtime_call::Config::RuntimeCall` and `pezframe_system::Config::RuntimeCall`
//! are two different representations of the same concrete type that is only known when the runtime
//! is being constructed.
//! `pezpallet_with_specific_runtime_call::Config::RuntimeCall` and
//! `pezframe_system::Config::RuntimeCall` are two different representations of the same concrete
//! type that is only known when the runtime is being constructed.
//!
//! Now, within this pezpallet, this new `RuntimeCall` can be used, and it can use its new trait
//! bounds, such as being [`frame::traits::IsSubType`]:
@@ -109,10 +110,10 @@
//! ### Asserting Equality of Multiple Runtime Composite Enums
//!
//! Recall that in the above example, `<T as Config>::RuntimeCall` and `<T as
//! pezframe_system::Config>::RuntimeCall` are expected to be equal types, but at the compile-time we
//! have to represent them with two different associated types with different bounds. Would it not
//! be cool if we had a test to make sure they actually resolve to the same concrete type once the
//! runtime is constructed? The following snippet exactly does that:
//! pezframe_system::Config>::RuntimeCall` are expected to be equal types, but at the compile-time
//! we have to represent them with two different associated types with different bounds. Would it
//! not be cool if we had a test to make sure they actually resolve to the same concrete type once
//! the runtime is constructed? The following snippet exactly does that:
#![doc = docify::embed!("./src/reference_docs/frame_runtime_types.rs", assert_equality)]
//!
//! We leave it to the reader to further explore what [`frame::traits::Hooks::integrity_test`] is,
@@ -39,9 +39,9 @@
//!
//! 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:
//! 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.
+23 -22
View File
@@ -50,20 +50,20 @@
//! trait implementations.
//!
//! **Traits** define common interfaces that types of tokens should implement. For example, the
//! [`fungible::Inspect`](`pezframe_support::traits::fungible::Inspect`) trait specifies an interface
//! for *inspecting* token state such as the total issuance of the token, the balance of individual
//! accounts, etc.
//! [`fungible::Inspect`](`pezframe_support::traits::fungible::Inspect`) trait specifies an
//! interface for *inspecting* token state such as the total issuance of the token, the balance of
//! individual accounts, etc.
//!
//! **Trait implementations** are concrete implementations of these traits. For example, one of the
//! many traits [`pezpallet_balances`] implements is
//! [`fungible::Inspect`](`pezframe_support::traits::fungible::Inspect`)[^1]. It provides the concrete
//! way of inspecting the total issuance, balance of accounts, etc. There can be many
//! [`fungible::Inspect`](`pezframe_support::traits::fungible::Inspect`)[^1]. It provides the
//! concrete way of inspecting the total issuance, balance of accounts, etc. There can be many
//! implementations of the same traits.
//!
//! [^1]: Rust Advanced Tip: The knowledge that [`pezpallet_balances`] implements
//! [`fungible::Inspect`](`pezframe_support::traits::fungible::Inspect`) is not some arcane knowledge
//! that you have to know by heart or memorize. One can simply look at the list of the implementors
//! of any trait in the Rust Doc to find all implementors (e.g.
//! [`fungible::Inspect`](`pezframe_support::traits::fungible::Inspect`) is not some arcane
//! knowledge that you have to know by heart or memorize. One can simply look at the list of the
//! implementors of any trait in the Rust Doc to find all implementors (e.g.
//! [Mutate trait implementors](https://docs.pezkuwichain.io/sdk/master/pezframe_support/traits/tokens/fungible/trait.Mutate.html#implementors)),
//! or use the `rust-analyzer`'s `Implementations` action.
//!
@@ -75,15 +75,16 @@
//! pezpallet may use [`pezpallet_balances`] in a tightly coupled manner, directly calling methods
//! on the pezpallet to reserve and unreserve deposits. This approach works well,
//! until someone has a use case requiring that an asset from a different pezpallet such as
//! [`pezpallet_assets`] is used for the deposit. Rather than tightly coupling [`pezpallet_preimage`] to
//! [`pezpallet_balances`], [`pezpallet_assets`], and every other token-handling pezpallet, a user
//! could possibly specify that [`pezpallet_preimage`] does not specify a concrete pezpallet as a
//! dependency, but instead accepts any dependency which implements the
//! [`pezpallet_assets`] is used for the deposit. Rather than tightly coupling
//! [`pezpallet_preimage`] to [`pezpallet_balances`], [`pezpallet_assets`], and every other
//! token-handling pezpallet, a user could possibly specify that [`pezpallet_preimage`] does not
//! specify a concrete pezpallet as a dependency, but instead accepts any dependency which
//! implements the
//! [`currency::ReservableCurrency`](`pezframe_support::traits::tokens::currency::ReservableCurrency`)
//! trait, namely via its [`Config::Currency`](`pezpallet_preimage::pezpallet::Config::Currency`)
//! associated type. This allows [`pezpallet_preimage`] to support any arbitrary pezpallet implementing
//! this trait, without needing any knowledge of what those pallets may be or requiring changes to
//! support new pallets which may be written in the future.
//! associated type. This allows [`pezpallet_preimage`] to support any arbitrary pezpallet
//! implementing this trait, without needing any knowledge of what those pallets may be or requiring
//! changes to support new pallets which may be written in the future.
//!
//! Read more about coupling, and the benefits of loose coupling
//! [here](crate::reference_docs::frame_pallet_coupling).
@@ -103,14 +104,14 @@
//!
//! ## Fungible Token Trait Implementations in FRAME
//!
//! [`pezpallet_balances`] implements [`fungible`](`pezframe_support::traits::fungible`), and is the most
//! commonly used fungible implementation in FRAME. Most of the time, it's used for managing the
//! native token of the blockchain network it's used in.
//! [`pezpallet_balances`] implements [`fungible`](`pezframe_support::traits::fungible`), and is the
//! most commonly used fungible implementation in FRAME. Most of the time, it's used for managing
//! the native token of the blockchain network it's used in.
//!
//! [`pezpallet_assets`] implements [`fungibles`](`pezframe_support::traits::fungibles`), and is another
//! popular fungible token implementation. It supports the creation and management of multiple
//! assets in a single crate, making it a good choice when a network requires more assets in
//! addition to its native token.
//! [`pezpallet_assets`] implements [`fungibles`](`pezframe_support::traits::fungibles`), and is
//! another popular fungible token implementation. It supports the creation and management of
//! multiple assets in a single crate, making it a good choice when a network requires more assets
//! in addition to its native token.
//!
//! ## Non-Fungible Tokens in FRAME
//!
+3 -3
View File
@@ -9,9 +9,9 @@
//! it is hard to know the types internal to the runtime, specifically in light of the fact that
//! they can change at any point in time.
//!
//! This is why all Bizinikiwi-based runtimes must expose a [`pezsp_api::Metadata`] api, which mandates
//! the runtime to return a description of itself. The return type of this api is `Vec<u8>`, meaning
//! that it is up to the runtime developer to decide on the format of this.
//! This is why all Bizinikiwi-based runtimes must expose a [`pezsp_api::Metadata`] api, which
//! mandates the runtime to return a description of itself. The return type of this api is
//! `Vec<u8>`, meaning that it is up to the runtime developer to decide on the format of this.
//!
//! All [`crate::pezkuwi_sdk::frame_runtime`] based runtimes expose a specific metadata language,
//! maintained in <https://github.com/paritytech/frame-metadata> which is adopted in the Pezkuwi
+2 -2
View File
@@ -60,8 +60,8 @@ pub mod defensive_programming;
/// `RuntimeCall`.
pub mod frame_runtime_types;
/// Learn about how to make a pezpallet/runtime that is fee-less and instead uses another mechanism to
/// control usage and sybil attacks.
/// Learn about how to make a pezpallet/runtime that is fee-less and instead uses another mechanism
/// to control usage and sybil attacks.
pub mod fee_less_runtime;
/// Learn about metadata, the main means through which an upgradeable runtime communicates its
+12 -12
View File
@@ -123,9 +123,9 @@
//! consensus engine to work, and that particular runtime-api is implemented by a pezpallet
//! corresponding to that consensus engine.
//!
//! For example, taking a snippet from [`pez_solochain_template_runtime`], the runtime has to provide
//! this additional runtime-api (compared to [`pez_minimal_template_runtime`]), if the node software is
//! configured to use the Aura consensus engine:
//! For example, taking a snippet from [`pez_solochain_template_runtime`], the runtime has to
//! provide this additional runtime-api (compared to [`pez_minimal_template_runtime`]), if the node
//! software is configured to use the Aura consensus engine:
//!
//! ```text
//! impl pezsp_consensus_aura::AuraApi<Block, AuraId> for Runtime {
@@ -148,14 +148,14 @@
//! * [`pezsc_consensus_manual_seal`]: Useful for testing, where any node can produce a block at any
//! time. This is often combined with a fixed interval at which a block is produced.
//! * [`pezsc_consensus_aura`]/[`pezpallet_aura`]: A simple round-robin block authoring mechanism.
//! * [`pezsc_consensus_babe`]/[`pezpallet_babe`]: A more advanced block authoring mechanism, capable of
//! anonymizing the next block author.
//! * [`pezsc_consensus_babe`]/[`pezpallet_babe`]: A more advanced block authoring mechanism,
//! capable of anonymizing the next block author.
//! * [`pezsc_consensus_pow`]: Proof of Work block authoring.
//!
//! For finality, there is one main option shipped with pezkuwi-sdk:
//!
//! * [`pezsc_consensus_grandpa`]/[`pezpallet_grandpa`]: A finality gadget that uses a voting mechanism to
//! decide when a block
//! * [`pezsc_consensus_grandpa`]/[`pezpallet_grandpa`]: A finality gadget that uses a voting
//! mechanism to decide when a block
//!
//! **The most important lesson here is that the node and the runtime must have matching consensus
//! components.**
@@ -185,11 +185,11 @@
//! failure.
//!
//! The list of checks may evolve in the future and for now only few rules are implemented:
//! * runtimes must define a type for [`pezcumulus-pezpallet-teyrchain-system`], which is recommended to
//! be named as `TeyrchainSystem`.
//! * runtimes must define a type for [`pezframe-system`] pezpallet, which is recommended to be named as
//! `System`. The configured [`block number`] here will be used by Omni Node to configure AURA
//! accordingly.
//! * runtimes must define a type for [`pezcumulus-pezpallet-teyrchain-system`], which is
//! recommended to be named as `TeyrchainSystem`.
//! * runtimes must define a type for [`pezframe-system`] pezpallet, which is recommended to be
//! named as `System`. The configured [`block number`] here will be used by Omni Node to configure
//! AURA accordingly.
//!
//! [`templates`]: crate::pezkuwi_sdk::templates
//! [`teyrchain-template`]: https://github.com/pezkuwichain/pezkuwi-sdk-teyrchain-template
@@ -48,8 +48,8 @@
//! and include the ones that are known to fit based on the worst case.
//!
//! The benchmarking code can be written as a part of FRAME pezpallet, using the macros provided in
//! [`pezframe_benchmarking`]. See any of the existing pallets in `pezkuwi-sdk`, or the pallets in our
//! [`crate::pezkuwi_sdk::templates`] for examples.
//! [`pezframe_benchmarking`]. See any of the existing pallets in `pezkuwi-sdk`, or the pallets in
//! our [`crate::pezkuwi_sdk::templates`] for examples.
//!
//! ## Weight
//!
@@ -9,11 +9,11 @@
//! section.
//! Moreover, we use the [`frame::traits::Get`].
//!
//! First, imagine we are writing a FRAME pezpallet. We represent this pezpallet with a `struct Pezpallet`,
//! and this pezpallet wants to implement the functionalities of that pezpallet, for example a simple
//! `transfer` function. For the sake of education, we are interested in having a `MinTransfer`
//! amount, expressed as a [`frame::traits::Get`], which will dictate what is the minimum amount
//! that can be transferred.
//! First, imagine we are writing a FRAME pezpallet. We represent this pezpallet with a `struct
//! Pezpallet`, and this pezpallet wants to implement the functionalities of that pezpallet, for
//! example a simple `transfer` function. For the sake of education, we are interested in having a
//! `MinTransfer` amount, expressed as a [`frame::traits::Get`], which will dictate what is the
//! minimum amount that can be transferred.
//!
//! We can foremost write this as simple as the following snippet:
#![doc = docify::embed!("./src/reference_docs/trait_based_programming.rs", basic)]
@@ -5,8 +5,8 @@
//!
//! FRAME by default already provides the following transaction extensions:
//!
//! - [`CheckGenesis`](pezframe_system::CheckGenesis): Ensures that a transaction was sent for the same
//! network. Determined based on genesis.
//! - [`CheckGenesis`](pezframe_system::CheckGenesis): Ensures that a transaction was sent for the
//! same network. Determined based on genesis.
//!
//! - [`CheckMortality`](pezframe_system::CheckMortality): Extends a transaction with a configurable
//! mortality.
@@ -14,44 +14,45 @@
//! - [`CheckNonZeroSender`](pezframe_system::CheckNonZeroSender): Ensures that the sender of a
//! transaction is not the *all zero account* (all bytes of the accountid are zero).
//!
//! - [`CheckNonce`](pezframe_system::CheckNonce): Extends a transaction with a nonce to prevent replay
//! of transactions and to provide ordering of transactions.
//! - [`CheckNonce`](pezframe_system::CheckNonce): Extends a transaction with a nonce to prevent
//! replay of transactions and to provide ordering of transactions.
//!
//! - [`CheckSpecVersion`](pezframe_system::CheckSpecVersion): Ensures that a transaction was built for
//! the currently active runtime.
//! - [`CheckSpecVersion`](pezframe_system::CheckSpecVersion): Ensures that a transaction was built
//! for the currently active runtime.
//!
//! - [`CheckTxVersion`](pezframe_system::CheckTxVersion): Ensures that the transaction signer used the
//! correct encoding of the call.
//! - [`CheckTxVersion`](pezframe_system::CheckTxVersion): Ensures that the transaction signer used
//! the correct encoding of the call.
//!
//! - [`CheckWeight`](pezframe_system::CheckWeight): Ensures that the transaction fits into the block
//! before dispatching it.
//! - [`CheckWeight`](pezframe_system::CheckWeight): Ensures that the transaction fits into the
//! block before dispatching it.
//!
//! - [`ChargeTransactionPayment`](pezpallet_transaction_payment::ChargeTransactionPayment): Charges
//! transaction fees from the signer based on the weight of the call using the native token.
//!
//! - [`ChargeAssetTxPayment`](pezpallet_asset_tx_payment::ChargeAssetTxPayment): Charges transaction
//! fees from the signer based on the weight of the call using any supported asset (including the
//! native token).
//! - [`ChargeAssetTxPayment`](pezpallet_asset_tx_payment::ChargeAssetTxPayment): Charges
//! transaction fees from the signer based on the weight of the call using any supported asset
//! (including the native token).
//!
//! - [`ChargeAssetTxPayment`(using
//! conversion)](pezpallet_asset_conversion_tx_payment::ChargeAssetTxPayment): Charges transaction
//! fees from the signer based on the weight of the call using any supported asset (including the
//! native token). The asset is converted to the native token using a pool.
//!
//! - [`SkipCheckIfFeeless`](pezpallet_skip_feeless_payment::SkipCheckIfFeeless): Allows transactions
//! to be processed without paying any fee. This requires that the `call` that should be
//! dispatched is augmented with the [`feeless_if`](pezframe_support::pezpallet_macros::feeless_if)
//! attribute.
//! - [`SkipCheckIfFeeless`](pezpallet_skip_feeless_payment::SkipCheckIfFeeless): Allows
//! transactions to be processed without paying any fee. This requires that the `call` that should
//! be dispatched is augmented with the
//! [`feeless_if`](pezframe_support::pezpallet_macros::feeless_if) attribute.
//!
//! - [`CheckMetadataHash`](pezframe_metadata_hash_extension::CheckMetadataHash): Extends transactions
//! to include the so-called metadata hash. This is required by chains to support the generic
//! Ledger application and other similar offline wallets.
//! - [`CheckMetadataHash`](pezframe_metadata_hash_extension::CheckMetadataHash): Extends
//! transactions to include the so-called metadata hash. This is required by chains to support the
//! generic Ledger application and other similar offline wallets.
//!
//! - [`WeightReclaim`](pezframe_system::WeightReclaim): A transaction extension for the relay chain
//! that reclaims unused weight after executing a transaction.
//!
//! - [`StorageWeightReclaim`](pezcumulus_pezpallet_weight_reclaim::StorageWeightReclaim): A transaction
//! extension for teyrchains that reclaims unused storage weight after executing a transaction.
//! - [`StorageWeightReclaim`](pezcumulus_pezpallet_weight_reclaim::StorageWeightReclaim): A
//! transaction extension for teyrchains that reclaims unused storage weight after executing a
//! transaction.
//!
//! For more information about these extensions, follow the link to the type documentation.
//!
@@ -63,12 +64,12 @@
#[docify::export]
pub mod transaction_extensions_example {
use codec::{Decode, DecodeWithMemTracking, Encode};
use scale_info::TypeInfo;
use pezsp_runtime::{
impl_tx_ext_default,
traits::{Dispatchable, TransactionExtension},
transaction_validity::TransactionValidityError,
};
use scale_info::TypeInfo;
// This doesn't actually check anything, but simply allows
// some arbitrary `u32` to be added to the extrinsic payload
@@ -43,7 +43,8 @@
//!
//! ## Usage
//!
//! > Note: You can see a live example in the `pezstaging-node-cli` and `pez-kitchensink-runtime` crates.
//! > Note: You can see a live example in the `pezstaging-node-cli` and `pez-kitchensink-runtime`
//! > crates.
//!
//! The umbrella crate can be added to your runtime crate like this:
//!
@@ -39,8 +39,8 @@
//! The node and the runtime need to communicate. This is done through two concepts:
//!
//! 1. **Host functions**: a way for the (WASM) runtime to talk to the node. All host functions are
//! defined in [`pezsp_io`]. For example, [`pezsp_io::storage`] are the set of host functions that
//! allow the runtime to read and write data to the on-chain state.
//! defined in [`pezsp_io`]. For example, [`pezsp_io::storage`] are the set of host functions
//! that allow the runtime to read and write data to the on-chain state.
//! 2. **Runtime APIs**: a way for the node to talk to the WASM runtime. Runtime APIs are defined
//! using macros and utilities in [`pezsp_api`]. For example, [`pezsp_api::Core`] is the most
//! fundamental runtime API that any blockchain must implement in order to be able to (re)
@@ -127,10 +127,10 @@
//! onchain. Else, nodes who run the native runtime will come to a different state transition. How
//! do nodes determine if two runtimes are the same? Through the very important
//! [`pezsp_version::RuntimeVersion`]. All runtimes expose their version via a runtime api
//! ([`pezsp_api::Core::version`]) that returns this struct. The node software, or other applications,
//! inspect this struct to examine the identity of a runtime, and to determine if two runtimes are
//! the same. Namely, [`pezsp_version::RuntimeVersion::spec_version`] is the main key that implies two
//! runtimes are the same.
//! ([`pezsp_api::Core::version`]) that returns this struct. The node software, or other
//! applications, inspect this struct to examine the identity of a runtime, and to determine if two
//! runtimes are the same. Namely, [`pezsp_version::RuntimeVersion::spec_version`] is the main key
//! that implies two runtimes are the same.
//!
//! Therefore, it is utmost important to make sure before any runtime upgrade, the spec version is
//! updated.