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