cumulus_pallet_aura_ext/
lib.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Cumulus.
3// SPDX-License-Identifier: Apache-2.0
4
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// 	http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
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 mod migration;
44mod test;
45
46pub use consensus_hook::FixedVelocityConsensusHook;
47
48type Aura<T> = pallet_aura::Pallet<T>;
49
50pub use pallet::*;
51
52#[frame_support::pallet]
53pub mod pallet {
54	use super::*;
55	use frame_support::pallet_prelude::*;
56	use frame_system::pallet_prelude::*;
57
58	/// The configuration trait.
59	#[pallet::config]
60	pub trait Config: pallet_aura::Config + frame_system::Config {}
61
62	#[pallet::pallet]
63	#[pallet::storage_version(migration::STORAGE_VERSION)]
64	pub struct Pallet<T>(_);
65
66	#[pallet::hooks]
67	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
68		fn on_finalize(_: BlockNumberFor<T>) {
69			// Update to the latest AuRa authorities.
70			Authorities::<T>::put(pallet_aura::Authorities::<T>::get());
71		}
72
73		fn on_initialize(_: BlockNumberFor<T>) -> Weight {
74			// Fetch the authorities once to get them into the storage proof of the PoV.
75			Authorities::<T>::get();
76
77			T::DbWeight::get().reads_writes(1, 0)
78		}
79	}
80
81	/// Serves as cache for the authorities.
82	///
83	/// The authorities in AuRa are overwritten in `on_initialize` when we switch to a new session,
84	/// but we require the old authorities to verify the seal when validating a PoV. This will
85	/// always be updated to the latest AuRa authorities in `on_finalize`.
86	#[pallet::storage]
87	pub(crate) type Authorities<T: Config> = StorageValue<
88		_,
89		BoundedVec<T::AuthorityId, <T as pallet_aura::Config>::MaxAuthorities>,
90		ValueQuery,
91	>;
92
93	/// Current relay chain slot paired with a number of authored blocks.
94	///
95	/// This is updated in [`FixedVelocityConsensusHook::on_state_proof`] with the current relay
96	/// chain slot as provided by the relay chain state proof.
97	#[pallet::storage]
98	pub(crate) type RelaySlotInfo<T: Config> = StorageValue<_, (Slot, u32), OptionQuery>;
99
100	#[pallet::genesis_config]
101	#[derive(frame_support::DefaultNoBound)]
102	pub struct GenesisConfig<T: Config> {
103		#[serde(skip)]
104		pub _config: core::marker::PhantomData<T>,
105	}
106
107	#[pallet::genesis_build]
108	impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
109		fn build(&self) {
110			let authorities = pallet_aura::Authorities::<T>::get();
111			Authorities::<T>::put(authorities);
112		}
113	}
114}
115
116/// The block executor used when validating a PoV at the relay chain.
117///
118/// When executing the block it will verify the block seal to ensure that the correct author created
119/// the block.
120pub struct BlockExecutor<T, I>(core::marker::PhantomData<(T, I)>);
121
122impl<Block, T, I> ExecuteBlock<Block> for BlockExecutor<T, I>
123where
124	Block: BlockT,
125	T: Config,
126	I: ExecuteBlock<Block>,
127{
128	fn execute_block(block: Block) {
129		let (mut header, extrinsics) = block.deconstruct();
130		// We need to fetch the authorities before we execute the block, to get the authorities
131		// before any potential update.
132		let authorities = Authorities::<T>::get();
133
134		let mut seal = None;
135		header.digest_mut().logs.retain(|s| {
136			let s =
137				CompatibleDigestItem::<<T::AuthorityId as RuntimeAppPublic>::Signature>::as_aura_seal(s);
138			match (s, seal.is_some()) {
139				(Some(_), true) => panic!("Found multiple AuRa seal digests"),
140				(None, _) => true,
141				(Some(s), false) => {
142					seal = Some(s);
143					false
144				},
145			}
146		});
147
148		let seal = seal.expect("Could not find an AuRa seal digest!");
149
150		let author = Aura::<T>::find_author(
151			header.digest().logs().iter().filter_map(|d| d.as_pre_runtime()),
152		)
153		.expect("Could not find AuRa author index!");
154
155		let pre_hash = header.hash();
156
157		if !authorities
158			.get(author as usize)
159			.unwrap_or_else(|| {
160				panic!("Invalid AuRa author index {} for authorities: {:?}", author, authorities)
161			})
162			.verify(&pre_hash, &seal)
163		{
164			panic!("Invalid AuRa seal");
165		}
166
167		I::execute_block(Block::new(header, extrinsics));
168	}
169}