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}