Skip to main content

Runtime

Now it's time to develop the second part of your Inherents project - the Runtime side.

Please head to the pallets folder as you're going to use and modify the template pallet as a base pallet. First of all change the template subfolder name to inherents-example.

The most of the necessary changes will refer to the lib.rs file where the pallet is defined.

Let's include some types:

use sp_std::vec::Vec;
use sp_inherents::{InherentIdentifier, IsFatalError};
use sp_core::{Encode, Decode};
use sp_runtime::traits::Zero;

/// Type for Scale-encoded data provided by the block author
type InherentRawType = Vec<u8>;

pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"ext_data";

You also added a private type alias InherentRawType which represents bytes vector of Scale-encoded inherent data passed from the Client.

The INHERENT_IDENTIFIER is the 8-byte-long key you already used on the Client side to put the encoded data into the storage.

The next thing is to add InherentDataType declaration into the Config trait;

#[pallet::config]
pub trait Config: frame_system::Config {
/// Because this pallet emits events, it depends on the runtime's definition of an event.
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
/// Type representing the weight of this pallet
type WeightInfo: WeightInfo;
/// Actual type of inherent data
type InherentDataType: Default + Encode + Decode + Clone + Parameter + Member + MaxEncodedLen;
}

and its corresponding definition for the Runtime implementing the Config trait in runtime/src/lib:

/* snip */
pub type InherentDataType = u16;

Now back to pallets/inherents-example/lib.rs, define the pallet's Storage Item:

// Storage items for inherent data created by the block author
#[pallet::storage]
pub type StoredInherentData<T: Config> = StorageValue<_, T::InherentDataType, OptionQuery>;

The next step is to define the unsigned extrinsic that will be submitted as a result of inherent receipt, and the corresponding Event that will be triggered upon extrinsic call success as well as the Error for the case of failure outcome:

#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
/// Triggered when 'set' transaction succedes
InherentDataSet { data: T::InherentDataType },
}

// Errors inform users that something went wrong.
#[pallet::error]
pub enum Error<T> {
/// Triggered when the inherent data is already set for the current block
AlreadySet,
}

#[pallet::call]
impl<T: Config> Pallet<T> {
/// Unsigned extrinsic submitted by create_inherent(..)
#[pallet::call_index(0)]
#[pallet::weight(T::WeightInfo::set())]
pub fn set(origin: OriginFor<T>, inherent_data: T::InherentDataType) -> DispatchResult {
// as this call is created by block auth it is supposed to be unsigned
ensure_none(origin)?;
ensure!(StoredInherentData::<T>::get().is_none(), Error::<T>::AlreadySet);

StoredInherentData::<T>::put(&inherent_data);

Self::deposit_event(Event::InherentDataSet { data: inherent_data });

Ok(())
}
}

The essential thing a pallet that provides and/or verifies an inherent extrinsic must do is to implement a ProvideInherent trait:

// This pallet provides an inherent, as such it implements ProvideInherent trait
// https://paritytech.github.io/substrate/master/frame_support/inherent/trait.ProvideInherent.html
#[pallet::inherent]
impl<T: Config> ProvideInherent for Pallet<T> {
type Call = Call<T>;
type Error = InherentError;
const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER;
// This method is used to decide whether this inherent is requiered for the block to be accepted
fn is_inherent_required(data: &InherentData) -> Result<Option<Self::Error>, Self::Error> {
// we could return Ok(None) to indicate that this inherent is not required.
// This happens by default if altenative implementation of is_inherent_required is provided
// Here for demonstration we return Ok(Some(..)) if inherent data is present and successfully decoded, expecting
// that inherent is required in this case.
Ok(Self::get_and_decode_data(data)
.map(|_| InherentError::InherentRequiredForDataPresent))
}

fn create_inherent(data: &InherentData) -> Option<Self::Call> {
// create and return the extrinsic call if the data could be read and decoded
Self::get_and_decode_data(data)
.map(|inherent_data| Call::set {inherent_data})
}
// Determine if a call is an inherent extrinsic
fn is_inherent(call: &Self::Call) -> bool {
matches!(call, Call::set {..})
}
}

And the final thing left to do is to implement and embed your pallet into the Runtime, as usual:

/// Configure the inherents-example in pallets/inherents_example.
impl inherents_example::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = inherents_example::weights::SubstrateWeight<Runtime>;
type InherentDataType = InherentDataType;
}
// Create the runtime by composing the FRAME pallets that were previously configured.
construct_runtime!(
pub struct Runtime
where
Block = Block,
NodeBlock = opaque::Block,
UncheckedExtrinsic = UncheckedExtrinsic,
{
/* snip */
InherentsExample: inherents_example,
}
);

grillchat icon