pallet_block_participation/
lib.rs1#![cfg_attr(not(feature = "std"), no_std)]
39#![deny(missing_docs)]
40
41#[cfg(feature = "runtime-benchmarks")]
42mod benchmarking;
43#[cfg(test)]
44mod mock;
45#[cfg(test)]
46mod tests;
47pub mod weights;
48
49use frame_support::pallet_prelude::*;
50pub use pallet::*;
51use sp_block_participation::*;
52
53pub trait BlockParticipationProvider<Moment, BlockProducer> {
55 fn blocks_to_process(moment: &Moment) -> impl Iterator<Item = (Moment, BlockProducer)>;
57
58 fn discard_processed_blocks(moment: &Moment);
60}
61
62#[frame_support::pallet]
63pub mod pallet {
64 use super::*;
65 use frame_system::pallet_prelude::*;
66 use sp_std::vec::Vec;
67
68 #[pallet::pallet]
69 pub struct Pallet<T>(_);
70
71 #[pallet::config]
72 pub trait Config: frame_system::Config {
73 type WeightInfo: crate::weights::WeightInfo;
75
76 type Moment: Parameter + Default + MaxEncodedLen + PartialOrd;
81
82 type BlockParticipationProvider: BlockParticipationProvider<Self::Moment, Self::BlockAuthor>;
87
88 type BlockAuthor: Member + Parameter + MaxEncodedLen;
90
91 type DelegatorId: Member + Parameter + MaxEncodedLen;
94
95 const TARGET_INHERENT_ID: InherentIdentifier;
99 }
100
101 #[pallet::storage]
102 pub type ProcessedUpTo<T: Config> = StorageValue<_, T::Moment, ValueQuery>;
103
104 #[pallet::error]
105 pub enum Error<T> {
106 MomentNotIncreasing,
108 }
109
110 #[pallet::inherent]
111 impl<T: Config> ProvideInherent for Pallet<T> {
112 type Call = Call<T>;
113 type Error = sp_block_participation::InherentError;
114 const INHERENT_IDENTIFIER: InherentIdentifier = sp_block_participation::INHERENT_IDENTIFIER;
115
116 fn create_inherent(data: &InherentData) -> Option<Self::Call> {
117 let up_to_moment = Self::decode_inherent_data(data).unwrap()?;
118 Some(Call::note_processing { up_to_moment })
119 }
120
121 fn check_inherent(call: &Self::Call, data: &InherentData) -> Result<(), Self::Error> {
122 let Some(expected_moment) = Self::decode_inherent_data(data)? else {
123 return Err(Self::Error::UnexpectedInherent);
124 };
125
126 let Self::Call::note_processing { up_to_moment } = call else {
127 unreachable!("There should be no other extrinsic in the pallet")
128 };
129
130 ensure!(expected_moment == *up_to_moment, Self::Error::InvalidInherentData);
131
132 Ok(())
133 }
134
135 fn is_inherent(call: &Self::Call) -> bool {
136 matches!(call, Call::note_processing { .. })
137 }
138
139 fn is_inherent_required(data: &InherentData) -> Result<Option<Self::Error>, Self::Error> {
140 if Self::decode_inherent_data(data)?.is_some() {
141 Ok(Some(Self::Error::InherentRequired))
142 } else {
143 Ok(None)
144 }
145 }
146 }
147
148 impl<T: Config> Pallet<T> {
149 fn decode_inherent_data(data: &InherentData) -> Result<Option<T::Moment>, InherentError> {
150 data.get_data(&Self::INHERENT_IDENTIFIER)
151 .map_err(|_| InherentError::InvalidInherentData)
152 }
153 }
154
155 #[pallet::call]
156 impl<T: Config> Pallet<T> {
157 #[pallet::call_index(0)]
163 #[pallet::weight((0, DispatchClass::Mandatory))]
164 pub fn note_processing(origin: OriginFor<T>, up_to_moment: T::Moment) -> DispatchResult {
165 ensure_none(origin)?;
166 ensure!(ProcessedUpTo::<T>::get() < up_to_moment, Error::<T>::MomentNotIncreasing);
167 log::info!("🧾 Processing block participation data");
168 T::BlockParticipationProvider::discard_processed_blocks(&up_to_moment);
169 ProcessedUpTo::<T>::set(up_to_moment);
170 Ok(())
171 }
172 }
173
174 impl<T: Config> Pallet<T> {
175 pub fn blocks_to_process(moment: &T::Moment) -> Vec<(T::Moment, T::BlockAuthor)> {
177 <T as Config>::BlockParticipationProvider::blocks_to_process(moment).collect()
178 }
179
180 pub fn discard_processed_blocks(moment: &T::Moment) {
182 T::BlockParticipationProvider::discard_processed_blocks(moment);
183 }
184
185 pub fn target_inherent_id() -> InherentIdentifier {
187 <T as Config>::TARGET_INHERENT_ID
188 }
189 }
190}