cumulus_pallet_aura_ext/
lib.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Cumulus.
3
4// Cumulus is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Cumulus is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Cumulus.  If not, see <http://www.gnu.org/licenses/>.
16
17//! Cumulus extension pallet for AuRa
18//!
19//! This pallet extends the Substrate AuRa pallet to make it compatible with parachains. It
20//! provides the [`Pallet`], the [`Config`] and the [`GenesisConfig`].
21//!
22//! It is also required that the parachain runtime uses the provided [`BlockExecutor`] to properly
23//! check the constructed block on the relay chain.
24//!
25//! ```
26//! # struct Runtime;
27//! # struct Executive;
28//! # struct CheckInherents;
29//! cumulus_pallet_parachain_system::register_validate_block! {
30//!     Runtime = Runtime,
31//!     BlockExecutor = cumulus_pallet_aura_ext::BlockExecutor::<Runtime, Executive>,
32//! }
33//! ```
34
35#![cfg_attr(not(feature = "std"), no_std)]
36
37use frame_support::traits::{ExecuteBlock, FindAuthor};
38use sp_application_crypto::RuntimeAppPublic;
39use sp_consensus_aura::{digests::CompatibleDigestItem, Slot};
40use sp_runtime::traits::{Block as BlockT, Header as HeaderT};
41
42pub mod consensus_hook;
43pub use consensus_hook::FixedVelocityConsensusHook;
44
45type Aura<T> = pallet_aura::Pallet<T>;
46
47pub use pallet::*;
48
49#[frame_support::pallet]
50pub mod pallet {
51	use super::*;
52	use frame_support::pallet_prelude::*;
53	use frame_system::pallet_prelude::*;
54
55	/// The configuration trait.
56	#[pallet::config]
57	pub trait Config: pallet_aura::Config + frame_system::Config {}
58
59	#[pallet::pallet]
60	pub struct Pallet<T>(_);
61
62	#[pallet::hooks]
63	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
64		fn on_finalize(_: BlockNumberFor<T>) {
65			// Update to the latest AuRa authorities.
66			Authorities::<T>::put(pallet_aura::Authorities::<T>::get());
67		}
68
69		fn on_initialize(_: BlockNumberFor<T>) -> Weight {
70			// Fetch the authorities once to get them into the storage proof of the PoV.
71			Authorities::<T>::get();
72
73			let new_slot = pallet_aura::CurrentSlot::<T>::get();
74
75			let (new_slot, authored) = match SlotInfo::<T>::get() {
76				Some((slot, authored)) if slot == new_slot => (slot, authored + 1),
77				Some((slot, _)) if slot < new_slot => (new_slot, 1),
78				Some(..) => {
79					panic!("slot moved backwards")
80				},
81				None => (new_slot, 1),
82			};
83
84			SlotInfo::<T>::put((new_slot, authored));
85
86			T::DbWeight::get().reads_writes(4, 2)
87		}
88	}
89
90	/// Serves as cache for the authorities.
91	///
92	/// The authorities in AuRa are overwritten in `on_initialize` when we switch to a new session,
93	/// but we require the old authorities to verify the seal when validating a PoV. This will
94	/// always be updated to the latest AuRa authorities in `on_finalize`.
95	#[pallet::storage]
96	pub(crate) type Authorities<T: Config> = StorageValue<
97		_,
98		BoundedVec<T::AuthorityId, <T as pallet_aura::Config>::MaxAuthorities>,
99		ValueQuery,
100	>;
101
102	/// Current slot paired with a number of authored blocks.
103	///
104	/// Updated on each block initialization.
105	#[pallet::storage]
106	pub(crate) type SlotInfo<T: Config> = StorageValue<_, (Slot, u32), OptionQuery>;
107
108	#[pallet::genesis_config]
109	#[derive(frame_support::DefaultNoBound)]
110	pub struct GenesisConfig<T: Config> {
111		#[serde(skip)]
112		pub _config: core::marker::PhantomData<T>,
113	}
114
115	#[pallet::genesis_build]
116	impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
117		fn build(&self) {
118			let authorities = pallet_aura::Authorities::<T>::get();
119			Authorities::<T>::put(authorities);
120		}
121	}
122}
123
124/// The block executor used when validating a PoV at the relay chain.
125///
126/// When executing the block it will verify the block seal to ensure that the correct author created
127/// the block.
128pub struct BlockExecutor<T, I>(core::marker::PhantomData<(T, I)>);
129
130impl<Block, T, I> ExecuteBlock<Block> for BlockExecutor<T, I>
131where
132	Block: BlockT,
133	T: Config,
134	I: ExecuteBlock<Block>,
135{
136	fn execute_block(block: Block) {
137		let (mut header, extrinsics) = block.deconstruct();
138		// We need to fetch the authorities before we execute the block, to get the authorities
139		// before any potential update.
140		let authorities = Authorities::<T>::get();
141
142		let mut seal = None;
143		header.digest_mut().logs.retain(|s| {
144			let s =
145				CompatibleDigestItem::<<T::AuthorityId as RuntimeAppPublic>::Signature>::as_aura_seal(s);
146			match (s, seal.is_some()) {
147				(Some(_), true) => panic!("Found multiple AuRa seal digests"),
148				(None, _) => true,
149				(Some(s), false) => {
150					seal = Some(s);
151					false
152				},
153			}
154		});
155
156		let seal = seal.expect("Could not find an AuRa seal digest!");
157
158		let author = Aura::<T>::find_author(
159			header.digest().logs().iter().filter_map(|d| d.as_pre_runtime()),
160		)
161		.expect("Could not find AuRa author index!");
162
163		let pre_hash = header.hash();
164
165		if !authorities
166			.get(author as usize)
167			.unwrap_or_else(|| {
168				panic!("Invalid AuRa author index {} for authorities: {:?}", author, authorities)
169			})
170			.verify(&pre_hash, &seal)
171		{
172			panic!("Invalid AuRa seal");
173		}
174
175		I::execute_block(Block::new(header, extrinsics));
176	}
177}