referrerpolicy=no-referrer-when-downgrade

pallet_validator_set_announcer/
lib.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// SPDX-License-Identifier: Apache-2.0
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// 	http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16//! Validator Set Announcer pallet.
17//!
18//! Applies the validator set of each era locally and hands it to [`Config::Sender`] for the other
19//! system chains. Meant for the chain that runs `pallet-staking-async`, typically Asset Hub.
20//!
21//! ## Overview
22//!
23//! The runtime calls [`Pallet::announce`] when a new era becomes active. It stores the set in the
24//! local `pallet-validator-collators`. If the receiver accepts it, the pallet hands the set to the
25//! sender once for every destination in [`Config::Destinations`] and deposits
26//! [`Event::AnnouncementSent`] or [`Event::AnnouncementFailed`] for each. A set the receiver
27//! rejects is reported with [`Event::AnnouncementRejected`] and not sent. Acceptance by the sender
28//! means the message was queued for delivery. Execution on the destination is not acknowledged.
29//!
30//! The pallet holds no storage. A failed send is not retried: every error an HRMP enqueue can
31//! return persists for the rest of the era, and the next era's announcement carries the full set
32//! again.
33
34#![cfg_attr(not(feature = "std"), no_std)]
35
36extern crate alloc;
37
38use frame_support::Parameter;
39use sp_staking::EraIndex;
40
41pub use pallet::*;
42
43/// Sends a validator set to one destination.
44pub trait SendValidatorSet<AccountId> {
45	/// Identifies a destination.
46	type Destination: Parameter;
47
48	/// Hand the validator set of `era` to the transport for `destination`.
49	///
50	/// `Ok` means the set was accepted for delivery, not that the destination applied it.
51	#[allow(clippy::result_unit_err)]
52	fn send(
53		destination: &Self::Destination,
54		era: EraIndex,
55		validators: &[AccountId],
56	) -> Result<(), ()>;
57
58	/// Prepare `destination` so that [`Self::send`] succeeds in benchmarks.
59	#[cfg(feature = "runtime-benchmarks")]
60	fn ensure_successful_send(_destination: &Self::Destination) {}
61}
62
63impl<AccountId> SendValidatorSet<AccountId> for () {
64	type Destination = ();
65
66	fn send(_: &(), _: EraIndex, _: &[AccountId]) -> Result<(), ()> {
67		Err(())
68	}
69}
70
71/// The destination type of the configured sender.
72pub type DestinationOf<T> = <<T as Config>::Sender as SendValidatorSet<
73	<T as frame_system::Config>::AccountId,
74>>::Destination;
75
76#[cfg(test)]
77mod mock;
78
79#[cfg(test)]
80mod tests;
81
82#[cfg(feature = "runtime-benchmarks")]
83mod benchmarking;
84pub mod weights;
85
86#[frame_support::pallet]
87pub mod pallet {
88	pub use crate::weights::WeightInfo;
89	use crate::{DestinationOf, SendValidatorSet};
90	use alloc::vec::Vec;
91	use frame_support::pallet_prelude::*;
92	use sp_staking::EraIndex;
93
94	#[pallet::pallet]
95	pub struct Pallet<T>(_);
96
97	/// Configuration trait of this pallet.
98	#[pallet::config]
99	pub trait Config:
100		pallet_validator_collators::Config + frame_system::Config<RuntimeEvent: From<Event<Self>>>
101	{
102		/// Sends an announced set to one destination.
103		type Sender: SendValidatorSet<Self::AccountId>;
104
105		/// The other system chains every announced set is sent to. This chain is served through
106		/// the receiver directly.
107		///
108		/// Every send is charged the weight measured for the first entry, so no entry may cost
109		/// more to send than it.
110		type Destinations: Get<Vec<DestinationOf<Self>>>;
111
112		/// Weight information for this pallet.
113		type WeightInfo: WeightInfo;
114	}
115
116	#[pallet::event]
117	#[pallet::generate_deposit(pub(super) fn deposit_event)]
118	pub enum Event<T: Config> {
119		/// The sender accepted the set of `era` for `destination`. Delivery and execution there
120		/// are not confirmed.
121		AnnouncementSent { destination: DestinationOf<T>, era: EraIndex },
122		/// The sender did not accept the set of `era` for `destination`. It is not retried.
123		AnnouncementFailed { destination: DestinationOf<T>, era: EraIndex },
124		/// The set of `era` was not announced because it was rejected with `error`.
125		AnnouncementRejected { era: EraIndex, error: DispatchError },
126	}
127
128	impl<T: Config> Pallet<T> {
129		/// Store the validator set of `era` locally and send it once to every destination.
130		///
131		/// A rejected set is reported with [`Event::AnnouncementRejected`] and not sent.
132		pub fn announce(era: EraIndex, validators: &[T::AccountId]) -> DispatchResult {
133			pallet_validator_collators::Pallet::<T>::receive_validator_set(
134				era,
135				validators.iter().cloned(),
136			)
137			.inspect_err(|&error| {
138				Self::deposit_event(Event::AnnouncementRejected { era, error });
139			})?;
140			for destination in T::Destinations::get() {
141				Self::send_to(destination, era, validators);
142			}
143			Ok(())
144		}
145
146		/// Upper bound of the weight of [`Self::announce`] for `validators` validators.
147		pub fn announce_weight(validators: u32) -> Weight {
148			let sends = T::Destinations::get().len() as u64;
149			<T as Config>::WeightInfo::announce(validators).saturating_add(
150				<T as Config>::WeightInfo::send_announcement(validators).saturating_mul(sends),
151			)
152		}
153
154		pub(crate) fn send_to(
155			destination: DestinationOf<T>,
156			era: EraIndex,
157			validators: &[T::AccountId],
158		) {
159			let event = match T::Sender::send(&destination, era, validators) {
160				Ok(()) => Event::AnnouncementSent { destination, era },
161				Err(()) => Event::AnnouncementFailed { destination, era },
162			};
163			Self::deposit_event(event);
164		}
165	}
166}