pezpallet_authorship/
lib.rs1#![cfg_attr(not(feature = "std"), no_std)]
23
24use pezframe_support::traits::FindAuthor;
25
26pub use pezpallet::*;
27
28#[impl_trait_for_tuples::impl_for_tuples(30)]
31pub trait EventHandler<Author, BlockNumber> {
32 fn note_author(author: Author);
34}
35
36#[pezframe_support::pezpallet]
37pub mod pezpallet {
38 use super::*;
39 use pezframe_support::pezpallet_prelude::*;
40 use pezframe_system::pezpallet_prelude::*;
41
42 #[pezpallet::config]
43 pub trait Config: pezframe_system::Config {
44 type FindAuthor: FindAuthor<Self::AccountId>;
46 type EventHandler: EventHandler<Self::AccountId, BlockNumberFor<Self>>;
48 }
49
50 #[pezpallet::pezpallet]
51 pub struct Pezpallet<T>(_);
52
53 #[pezpallet::hooks]
54 impl<T: Config> Hooks<BlockNumberFor<T>> for Pezpallet<T> {
55 fn on_initialize(_: BlockNumberFor<T>) -> Weight {
56 if let Some(author) = Self::author() {
57 T::EventHandler::note_author(author);
58 }
59
60 Weight::zero()
61 }
62
63 fn on_finalize(_: BlockNumberFor<T>) {
64 <Author<T>>::kill();
66 }
67 }
68
69 #[pezpallet::storage]
70 #[pezpallet::whitelist_storage]
71 pub(super) type Author<T: Config> = StorageValue<_, T::AccountId, OptionQuery>;
73}
74
75impl<T: Config> Pezpallet<T> {
76 pub fn author() -> Option<T::AccountId> {
81 if let Some(author) = <Author<T>>::get() {
83 return Some(author);
84 }
85
86 let digest = <pezframe_system::Pezpallet<T>>::digest();
87 let pre_runtime_digests = digest.logs.iter().filter_map(|d| d.as_pre_runtime());
88 T::FindAuthor::find_author(pre_runtime_digests).inspect(|a| {
89 <Author<T>>::put(&a);
90 })
91 }
92}
93
94#[cfg(test)]
95mod tests {
96 use super::*;
97 use crate as pezpallet_authorship;
98 use codec::{Decode, Encode};
99 use pezframe_support::{derive_impl, ConsensusEngineId};
100 use pezsp_core::H256;
101 use pezsp_runtime::{
102 generic::DigestItem, testing::Header, traits::Header as HeaderT, BuildStorage,
103 };
104
105 type Block = pezframe_system::mocking::MockBlock<Test>;
106
107 pezframe_support::construct_runtime!(
108 pub enum Test
109 {
110 System: pezframe_system,
111 Authorship: pezpallet_authorship,
112 }
113 );
114
115 #[derive_impl(pezframe_system::config_preludes::TestDefaultConfig)]
116 impl pezframe_system::Config for Test {
117 type Block = Block;
118 }
119
120 impl pezpallet::Config for Test {
121 type FindAuthor = AuthorGiven;
122 type EventHandler = ();
123 }
124
125 const TEST_ID: ConsensusEngineId = [1, 2, 3, 4];
126
127 pub struct AuthorGiven;
128
129 impl FindAuthor<u64> for AuthorGiven {
130 fn find_author<'a, I>(digests: I) -> Option<u64>
131 where
132 I: 'a + IntoIterator<Item = (ConsensusEngineId, &'a [u8])>,
133 {
134 for (id, mut data) in digests {
135 if id == TEST_ID {
136 return u64::decode(&mut data).ok();
137 }
138 }
139
140 None
141 }
142 }
143
144 fn seal_header(mut header: Header, author: u64) -> Header {
145 {
146 let digest = header.digest_mut();
147 digest.logs.push(DigestItem::PreRuntime(TEST_ID, author.encode()));
148 digest.logs.push(DigestItem::Seal(TEST_ID, author.encode()));
149 }
150
151 header
152 }
153
154 fn create_header(number: u64, parent_hash: H256, state_root: H256) -> Header {
155 Header::new(number, Default::default(), state_root, parent_hash, Default::default())
156 }
157
158 fn new_test_ext() -> pezsp_io::TestExternalities {
159 let t = pezframe_system::GenesisConfig::<Test>::default().build_storage().unwrap();
160 t.into()
161 }
162
163 #[test]
164 fn sets_author_lazily() {
165 new_test_ext().execute_with(|| {
166 let author = 42;
167 let mut header =
168 seal_header(create_header(1, Default::default(), [1; 32].into()), author);
169
170 header.digest_mut().pop(); System::reset_events();
172 System::initialize(&1, &Default::default(), header.digest());
173
174 assert_eq!(Authorship::author(), Some(author));
175 });
176 }
177}