referrerpolicy=no-referrer-when-downgrade

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//! cumulus_pallet_parachain_system::register_validate_block! {
29//!     Runtime = Runtime,
30//!     BlockExecutor = cumulus_pallet_aura_ext::BlockExecutor::<Runtime, Executive>,
31//! }
32//! ```
33
34#![cfg_attr(not(feature = "std"), no_std)]
35
36use frame_support::traits::{ExecuteBlock, FindAuthor};
37use sp_application_crypto::RuntimeAppPublic;
38use sp_consensus_aura::{digests::CompatibleDigestItem, Slot};
39use sp_runtime::traits::{Block as BlockT, Header as HeaderT, LazyBlock};
40
41pub mod consensus_hook;
42pub mod migration;
43pub mod signature_verifier;
44#[cfg(test)]
45mod test;
46
47pub use consensus_hook::FixedVelocityConsensusHook;
48pub use signature_verifier::AuraSchedulingVerifier;
49
50type Aura<T> = pallet_aura::Pallet<T>;
51
52pub use pallet::*;
53
54#[frame_support::pallet]
55pub mod pallet {
56	use super::*;
57	use frame_support::pallet_prelude::*;
58	use frame_system::pallet_prelude::*;
59
60	/// The configuration trait.
61	#[pallet::config]
62	pub trait Config: pallet_aura::Config + frame_system::Config {}
63
64	#[pallet::pallet]
65	#[pallet::storage_version(migration::STORAGE_VERSION)]
66	pub struct Pallet<T>(_);
67
68	#[pallet::hooks]
69	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
70		fn on_finalize(_: BlockNumberFor<T>) {
71			// Update to the latest AuRa authorities.
72			Authorities::<T>::put(pallet_aura::Authorities::<T>::get());
73		}
74
75		fn on_initialize(_: BlockNumberFor<T>) -> Weight {
76			// Fetch the authorities once to get them into the storage proof of the PoV.
77			Authorities::<T>::get();
78
79			T::DbWeight::get().reads_writes(1, 0)
80		}
81	}
82
83	/// Serves as cache for the authorities.
84	///
85	/// The authorities in AuRa are overwritten in `on_initialize` when we switch to a new session,
86	/// but we require the old authorities to verify the seal when validating a PoV. This will
87	/// always be updated to the latest AuRa authorities in `on_finalize`.
88	#[pallet::storage]
89	pub(crate) type Authorities<T: Config> = StorageValue<
90		_,
91		BoundedVec<T::AuthorityId, <T as pallet_aura::Config>::MaxAuthorities>,
92		ValueQuery,
93	>;
94
95	/// Current relay chain slot paired with a number of authored blocks.
96	///
97	/// This is updated in [`FixedVelocityConsensusHook::on_state_proof`] with the current relay
98	/// chain slot as provided by the relay chain state proof.
99	#[pallet::storage]
100	pub(crate) type RelaySlotInfo<T: Config> = StorageValue<_, (Slot, u32), OptionQuery>;
101
102	#[pallet::genesis_config]
103	#[derive(frame_support::DefaultNoBound)]
104	pub struct GenesisConfig<T: Config> {
105		#[serde(skip)]
106		pub _config: core::marker::PhantomData<T>,
107	}
108
109	#[pallet::genesis_build]
110	impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
111		fn build(&self) {
112			let authorities = pallet_aura::Authorities::<T>::get();
113			Authorities::<T>::put(authorities);
114		}
115	}
116}
117
118/// The block executor used when validating a PoV at the relay chain.
119///
120/// When executing the block it will verify the block seal to ensure that the correct author created
121/// the block.
122pub struct BlockExecutor<T, I>(core::marker::PhantomData<(T, I)>);
123
124impl<Block, T, I> ExecuteBlock<Block> for BlockExecutor<T, I>
125where
126	Block: BlockT,
127	T: Config,
128	I: ExecuteBlock<Block>,
129{
130	fn verify_and_remove_seal(block: &mut <Block as BlockT>::LazyBlock) {
131		let header = block.header_mut();
132		// We need to fetch the authorities before we execute the block, to get the authorities
133		// before any potential update.
134		let authorities = Authorities::<T>::get();
135
136		let mut seal = None;
137		header.digest_mut().logs.retain(|s| {
138			let s =
139				CompatibleDigestItem::<<T::AuthorityId as RuntimeAppPublic>::Signature>::as_aura_seal(s);
140			match (s, seal.is_some()) {
141				(Some(_), true) => panic!("Found multiple AuRa seal digests"),
142				(None, _) => true,
143				(Some(s), false) => {
144					seal = Some(s);
145					false
146				},
147			}
148		});
149
150		let seal = seal.expect("Could not find an AuRa seal digest!");
151
152		let author = Aura::<T>::find_author(
153			header.digest().logs().iter().filter_map(|d| d.as_pre_runtime()),
154		)
155		.expect("Could not find AuRa author index!");
156
157		let pre_hash = header.hash();
158
159		if !authorities
160			.get(author as usize)
161			.unwrap_or_else(|| {
162				panic!("Invalid AuRa author index {} for authorities: {:?}", author, authorities)
163			})
164			.verify(&pre_hash, &seal)
165		{
166			panic!("Invalid AuRa seal");
167		}
168	}
169
170	fn execute_verified_block(block: Block::LazyBlock) {
171		I::execute_verified_block(block);
172	}
173}