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}