Crate sp_inherents

source ·
Expand description

Substrate Inherent Extrinsics

Inherent extrinsics are extrinsics that are inherently added to each block. However, it is up to the runtime implementation to require an inherent for each block or to make it optional. Inherents are mainly used to pass data from the block producer to the runtime. So, inherents require some part that is running on the client side and some part that is running on the runtime side. Any data that is required by an inherent is passed as InherentData from the client to the runtime when the inherents are constructed.

The process of constructing and applying inherents is the following:

  1. The block producer first creates the InherentData by using the inherent data providers that are created by CreateInherentDataProviders.

  2. The InherentData is passed to the inherent_extrinsics function of the BlockBuilder runtime api. This will call the runtime which will create all the inherents that should be applied to the block.

  3. Apply each inherent to the block like any normal extrinsic.

On block import the inherents in the block are checked by calling the check_inherents runtime API. This will also pass an instance of InherentData which the runtime can use to validate all inherents. If some inherent data isn’t required for validating an inherent, it can be omitted when providing the inherent data providers for block import.

Providing inherent data

To provide inherent data from the client side, InherentDataProvider should be implemented.

use codec::Decode;
use sp_inherents::{InherentIdentifier, InherentData};

// This needs to be unique for the runtime.
const INHERENT_IDENTIFIER: InherentIdentifier = *b"testinh0";

/// Some custom inherent data provider
struct InherentDataProvider;

#[async_trait::async_trait]
impl sp_inherents::InherentDataProvider for InherentDataProvider {
	async fn provide_inherent_data(
		&self,
		inherent_data: &mut InherentData,
	) -> Result<(), sp_inherents::Error> {
		// We can insert any data that implements [`codec::Encode`].
		inherent_data.put_data(INHERENT_IDENTIFIER, &"hello")
	}

	/// When validating the inherents, the runtime implementation can throw errors. We support
	/// two error modes, fatal and non-fatal errors. A fatal error means that the block is invalid
	/// and this function here should return `Err(_)` to not import the block. Non-fatal errors
	/// are allowed to be handled here in this function and the function should return `Ok(())`
	/// if it could be handled. A non-fatal error is for example that a block is in the future
	/// from the point of view of the local node. In such a case the block import for example
	/// should be delayed until the block is valid.
	///
	/// If this functions returns `None`, it means that it is not responsible for this error or
	/// that the error could not be interpreted.
	async fn try_handle_error(
		&self,
		identifier: &InherentIdentifier,
		mut error: &[u8],
	) -> Option<Result<(), sp_inherents::Error>> {
		// Check if this error belongs to us.
		if *identifier != INHERENT_IDENTIFIER {
			return None;
		}

		// For demonstration purposes we are using a `String` as error type. In real
		// implementations it is advised to not use `String`.
		Some(Err(
			sp_inherents::Error::Application(Box::from(String::decode(&mut error).ok()?))
		))
	}
}

In the service the relevant inherent data providers need to be passed the block production and the block import. As already highlighted above, the providers can be different between import and production.


async fn cool_consensus_block_production(
	// The second parameter to the trait are parameters that depend on what the caller
	// can provide on extra data.
	_: impl sp_inherents::CreateInherentDataProviders<Block, ()>,
) {
	// do cool stuff
}

async fn cool_consensus_block_import(
	_: impl sp_inherents::CreateInherentDataProviders<Block, ()>,
) {
	// do cool stuff
}

async fn build_service(is_validator: bool) {
	// For block import we don't pass any inherent data provider, because our runtime
	// does not need any inherent data to validate the inherents.
	let block_import = cool_consensus_block_import(|_parent, ()| async { Ok(()) });

	let block_production = if is_validator {
		// For block production we want to provide our inherent data provider
		cool_consensus_block_production(|_parent, ()| async {
			Ok(InherentDataProvider)
		}).boxed()
	} else {
		futures::future::pending().boxed()
	};

	futures::pin_mut!(block_import);

	futures::future::select(block_import, block_production).await;
}

Creating the inherent

As the inherents are created by the runtime, it depends on the runtime implementation on how to create the inherents. As already described above the client side passes the InherentData and expects the runtime to construct the inherents out of it. When validating the inherents, CheckInherentsResult is used to communicate the result client side.

Structs

Enums

  • Errors that occur in context of inherents.

Traits

Type Definitions