sp_block_production_log/
lib.rs

1//! Primitives and support crate for `pallet_block_production_log`.
2//!
3//! This crate defines the primitive types and the inherent data provider for the block production log feature.
4//!
5//! ## Usage
6//!
7//! This crate supports operation of `pallet_block_production_log`.
8//! Consult the pallet's documentation on how to include it in the runtime.
9//!
10//! ### Adding to the node
11//!
12//! #### Implementing the runtime API
13//!
14//! The block production log feature requires [BlockProductionLogApi] to be implemented by the Partner Chain runtime
15//! so the current block author can be identified. The concrete implementation must use or match the mechanism of
16//! block author selection used by the particular Partner Chain's consensus mechanism. The API should use the same
17//! type `CommitteeMember` to represent the block author as was configured in pallet's configuration.
18//!
19//! An example for a Partner Chain using Aura consensus looks like this:
20//! ```rust, ignore
21//! impl_runtime_apis! {
22//!     impl BlockProductionLogApi<Block, CommitteeMember> for Runtime {
23//!         fn get_author(slot: Slot) -> Option<CommitteeMember> {
24//!             SessionCommitteeManagement::get_current_authority_round_robin(*slot as usize)
25//!         }
26//!     }
27//! }
28//! ```
29//! using the `pallet_session_committee_management::Pallet::get_current_authority_round_robin` function
30//! which performs the same round-robin author selection that Aura does internally.
31//!
32//! #### Adding the inherent data provider
33//!
34//! The inherent data provider should be added to the node's `CreateInherentDataProviders` implementation for
35//! both proposal and validation of blocks.  eg.:
36//!
37//! ```rust,ignore
38//! // Create the inherent data provider. `slot` must be the slot number of the block currently being produced/verified
39//! let block_author_idp = BlockAuthorInherentProvider::new(client.as_ref(), parent_hash, slot)?;
40//! ...
41//! // Return the inherent data provider together with other IDPs
42//! Ok((timestamp_idp, slot_idp, ..., block_author_idp, ...))
43//! ```
44//!
45//! The inherent data provider created using `BlockAuthorInherentProvider::new` will check whether `BlockProductionLogApi`
46//! is available in the runtime and will only provide inherent data if the API is present.
47//!
48
49#![cfg_attr(not(feature = "std"), no_std)]
50#![deny(missing_docs)]
51
52extern crate alloc;
53
54#[cfg(test)]
55mod test;
56
57use parity_scale_codec::{Decode, Encode};
58use sp_inherents::{InherentIdentifier, IsFatalError};
59#[cfg(feature = "std")]
60use {
61	sp_api::{ApiExt, ProvideRuntimeApi},
62	sp_inherents::InherentData,
63	sp_runtime::traits::Block as BlockT,
64};
65
66/// Inherent identifier used by the Block Production Log pallet
67pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"blprdlog";
68
69/// Error type used for failing calls of the block production log feature's inherent.
70#[derive(Encode, PartialEq)]
71#[cfg_attr(not(feature = "std"), derive(Debug))]
72#[cfg_attr(feature = "std", derive(Decode, thiserror::Error, sp_runtime::RuntimeDebug))]
73pub enum InherentError {
74	/// Inherent was not produced when expected
75	#[cfg_attr(
76		feature = "std",
77		error("Block Author inherent must be provided every block after initialization")
78	)]
79	InherentRequired,
80	/// Block Author inherent data is not correctly encoded
81	#[cfg_attr(feature = "std", error("Block Author inherent data is not correctly encoded"))]
82	InvalidInherentData,
83}
84impl IsFatalError for InherentError {
85	fn is_fatal_error(&self) -> bool {
86		true
87	}
88}
89
90/// Inherent data provider providing the block author of the current block
91/// Type parameters:
92/// - `Author`: Type representing a block author.
93#[cfg(feature = "std")]
94#[derive(Debug)]
95pub struct BlockAuthorInherentProvider<Author> {
96	/// Optional value of the current block author. Inherent data is not provided if [None].
97	pub author: Option<Author>,
98}
99
100#[cfg(feature = "std")]
101impl<Author> BlockAuthorInherentProvider<Author> {
102	/// Creates a new [BlockAuthorInherentProvider] using runtime API [BlockProductionLogApi].
103	///
104	/// The inherent data provider returned will be inert if [BlockProductionLogApi] is not detected in the runtime.
105	pub fn new<C, Member, Block>(
106		client: &C,
107		parent_hash: Block::Hash,
108		slot: sidechain_slots::Slot,
109	) -> Result<Self, Box<dyn core::error::Error + Send + Sync>>
110	where
111		Member: Decode,
112		Block: BlockT,
113		C: ProvideRuntimeApi<Block>,
114		C::Api: BlockProductionLogApi<Block, Member>,
115		Author: From<Member>,
116	{
117		let api = client.runtime_api();
118		if !api.has_api::<dyn BlockProductionLogApi<Block, Member>>(parent_hash)? {
119			return Ok(Self { author: None });
120		}
121		let author = client.runtime_api().get_author(parent_hash, slot)?.map(Author::from);
122
123		Ok(BlockAuthorInherentProvider { author })
124	}
125}
126
127#[cfg(feature = "std")]
128#[async_trait::async_trait]
129impl<T> sp_inherents::InherentDataProvider for BlockAuthorInherentProvider<T>
130where
131	T: Send + Sync + Encode + Decode,
132{
133	async fn provide_inherent_data(
134		&self,
135		inherent_data: &mut InherentData,
136	) -> Result<(), sp_inherents::Error> {
137		if let Some(author) = &self.author {
138			inherent_data.put_data(INHERENT_IDENTIFIER, author)?;
139		}
140		Ok(())
141	}
142
143	async fn try_handle_error(
144		&self,
145		identifier: &InherentIdentifier,
146		mut error: &[u8],
147	) -> Option<Result<(), sp_inherents::Error>> {
148		if identifier == &INHERENT_IDENTIFIER {
149			let error = InherentError::decode(&mut error).ok()?;
150			Some(Err(sp_inherents::Error::Application(Box::from(error))))
151		} else {
152			None
153		}
154	}
155}
156
157sp_api::decl_runtime_apis! {
158	/// Runtime API exposing data required for the [BlockAuthorInherentProvider] to operate.
159	/// Type parameters:
160	/// - `Member`: type representing a committee member eligible to be a block author. This type should correspond
161	///             to what is configured as the block author type used by the pallet.
162	pub trait BlockProductionLogApi<Member>
163	where
164		Member: Decode
165	{
166		/// Function returning the current block's author.
167		///
168		/// Its implementation must either use data exposed by the consensus mechanism used by the Partner Chain,
169		/// independently calculate it, or obtain it from another source.
170		///
171		/// Parameters:
172		/// - `slot`: slot number of the block currently being produced
173		fn get_author(slot: sidechain_slots::Slot) -> Option<Member>;
174	}
175}