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