1#![cfg_attr(not(feature = "std"), no_std)]
23
24use frame_support::traits::FindAuthor;
25
26pub use pallet::*;
27
28#[impl_trait_for_tuples::impl_for_tuples(30)]
31pub trait EventHandler<Author, BlockNumber> {
32 fn note_author(author: Author);
34}
35
36#[frame_support::pallet]
37pub mod pallet {
38 use super::*;
39 use frame_support::pallet_prelude::*;
40 use frame_system::pallet_prelude::*;
41
42 #[pallet::config]
43 pub trait Config: frame_system::Config {
44 type FindAuthor: FindAuthor<Self::AccountId>;
46 type EventHandler: EventHandler<Self::AccountId, BlockNumberFor<Self>>;
48 }
49
50 #[pallet::pallet]
51 pub struct Pallet<T>(_);
52
53 #[pallet::hooks]
54 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<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 #[pallet::storage]
70 #[pallet::whitelist_storage]
71 pub(super) type Author<T: Config> = StorageValue<_, T::AccountId, OptionQuery>;
73}
74
75impl<T: Config> Pallet<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 = <frame_system::Pallet<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 pallet_authorship;
98 use codec::{Decode, Encode};
99 use frame_support::{derive_impl, ConsensusEngineId};
100 use sp_core::H256;
101 use sp_runtime::{
102 generic::DigestItem, testing::Header, traits::Header as HeaderT, BuildStorage,
103 };
104
105 type Block = frame_system::mocking::MockBlock<Test>;
106
107 frame_support::construct_runtime!(
108 pub enum Test
109 {
110 System: frame_system,
111 Authorship: pallet_authorship,
112 }
113 );
114
115 #[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
116 impl frame_system::Config for Test {
117 type Block = Block;
118 }
119
120 impl pallet::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() -> sp_io::TestExternalities {
159 let t = frame_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}