sp_session_validator_management/
lib.rs

1//! # Primitives for Partner Chain committee selection.
2//!
3//! This crate implements shared types and traits used to implement Partner Chain committee rotation.
4
5#![cfg_attr(not(feature = "std"), no_std)]
6#![deny(missing_docs)]
7
8extern crate alloc;
9
10use alloc::vec::Vec;
11#[cfg(feature = "std")]
12use core::str::FromStr;
13use parity_scale_codec::DecodeWithMemTracking;
14use scale_info::TypeInfo;
15use sidechain_domain::{
16	CandidateRegistrations, DParameter, EpochNonce, MainchainAddress, PermissionedCandidateData,
17	PolicyId, StakePoolPublicKey, byte_string::SizedByteString,
18};
19use sp_core::{Decode, Encode, MaxEncodedLen};
20use sp_inherents::{InherentIdentifier, IsFatalError};
21
22/// Inherent identifier used by the Committee Selection pallet
23pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"/ariadne";
24
25#[derive(Encode, sp_runtime::RuntimeDebug, PartialEq, Eq)]
26#[cfg_attr(feature = "std", derive(Decode, thiserror::Error))]
27/// Error type used for failing calls of the Committee Selection inherent.
28pub enum InherentError {
29	#[deprecated(
30		since = "1.5.0",
31		note = "Use InvalidValidatorsMatchingHash or InvalidValidatorsHashMismatch"
32	)]
33	#[cfg_attr(
34		feature = "std",
35		error("The validators in the block do not match the calculated validators")
36	)]
37	/// The validators in the block do not match the calculated validators
38	InvalidValidators,
39	#[cfg_attr(
40		feature = "std",
41		error("Candidates inherent required: committee needs to be stored one epoch in advance")
42	)]
43	/// Candidates inherent required: committee needs to be stored one epoch in advance
44	CommitteeNeedsToBeStoredOneEpochInAdvance,
45	#[cfg_attr(
46		feature = "std",
47		error("The validators in the block do not match the calculated validators. Input data hash ({}) is valid.", .0.to_hex_string())
48	)]
49	/// The validators in the block do not match the calculated validators, but the input data hash is valid.
50	InvalidValidatorsMatchingHash(SizedByteString<32>),
51	#[cfg_attr(
52		feature = "std",
53		error("The validators and input data hash in the block do not match the calculated values. Expected hash: {}, got: {}",
54			.0.to_hex_string(),
55			.1.to_hex_string())
56	)]
57	/// The validators and input data hash in the block do not match the calculated values.
58	InvalidValidatorsHashMismatch(SizedByteString<32>, SizedByteString<32>),
59}
60
61impl IsFatalError for InherentError {
62	fn is_fatal_error(&self) -> bool {
63		true
64	}
65}
66
67#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
68#[derive(
69	Clone,
70	Encode,
71	Decode,
72	DecodeWithMemTracking,
73	TypeInfo,
74	MaxEncodedLen,
75	Debug,
76	PartialEq,
77	Eq,
78	PartialOrd,
79	Ord,
80)]
81/// Type representing committee members, either permissioned or registered
82pub enum CommitteeMember<AuthorityId, AuthorityKeys> {
83	/// A permissioned candidate
84	Permissioned {
85		/// Authority id of the candidate
86		id: AuthorityId,
87		/// Authority keys of the candidate
88		keys: AuthorityKeys,
89	},
90	/// A registered candidate
91	Registered {
92		/// Authority id of the candidate
93		id: AuthorityId,
94		/// Authority keys of the candidate
95		keys: AuthorityKeys,
96		/// Stake pool pub key of the candidate
97		stake_pool_pub_key: StakePoolPublicKey,
98	},
99}
100
101impl<AuthorityId, AuthorityKeys> From<(AuthorityId, AuthorityKeys)>
102	for CommitteeMember<AuthorityId, AuthorityKeys>
103{
104	fn from((id, keys): (AuthorityId, AuthorityKeys)) -> Self {
105		Self::Permissioned { id, keys }
106	}
107}
108
109impl<AuthorityId, AuthorityKeys> CommitteeMember<AuthorityId, AuthorityKeys> {
110	/// Constructs new permissioned candidate
111	pub fn permissioned(id: AuthorityId, keys: AuthorityKeys) -> Self {
112		Self::Permissioned { id, keys }
113	}
114
115	/// Returns the authority ID of the committee member
116	pub fn authority_id(&self) -> AuthorityId
117	where
118		AuthorityId: Clone,
119	{
120		match self {
121			Self::Permissioned { id, .. } => id.clone(),
122			Self::Registered { id, .. } => id.clone(),
123		}
124	}
125
126	/// Returns the authority keys of the committee member
127	pub fn authority_keys(&self) -> AuthorityKeys
128	where
129		AuthorityKeys: Clone,
130	{
131		match self {
132			Self::Permissioned { keys, .. } => keys.clone(),
133			Self::Registered { keys, .. } => keys.clone(),
134		}
135	}
136
137	/// Maps the authority keys
138	pub fn map_authority_keys<NewKeys>(
139		self,
140		f: impl Fn(AuthorityKeys) -> NewKeys,
141	) -> CommitteeMember<AuthorityId, NewKeys> {
142		match self {
143			Self::Permissioned { id, keys } => CommitteeMember::Permissioned { id, keys: f(keys) },
144			Self::Registered { id, keys, stake_pool_pub_key } => {
145				CommitteeMember::Registered { id, keys: f(keys), stake_pool_pub_key }
146			},
147		}
148	}
149}
150
151#[cfg(feature = "std")]
152impl From<InherentError> for sp_inherents::Error {
153	fn from(value: InherentError) -> Self {
154		sp_inherents::Error::Application(Box::from(value))
155	}
156}
157
158#[derive(Default, Debug, Clone, PartialEq, Eq, TypeInfo, Encode, Decode, MaxEncodedLen)]
159#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
160/// Collection of all mainchain script info needed for committee selection
161pub struct MainChainScripts {
162	/// [MainchainAddress] where registration UTXOs are located
163	pub committee_candidate_address: MainchainAddress,
164	/// [PolicyId] of D-parameter script
165	pub d_parameter_policy_id: PolicyId,
166	/// [PolicyId] of Permissioned Candidates script
167	pub permissioned_candidates_policy_id: PolicyId,
168}
169
170#[cfg(feature = "std")]
171impl MainChainScripts {
172	/// Reads [MainChainScripts] from env vars:
173	/// - COMMITTEE_CANDIDATE_ADDRESS
174	/// - D_PARAMETER_POLICY_ID
175	/// - PERMISSIONED_CANDIDATES_POLICY_ID
176	pub fn read_from_env() -> Result<MainChainScripts, envy::Error> {
177		#[derive(serde::Serialize, serde::Deserialize)]
178		pub struct MainChainScriptsEnvConfig {
179			pub committee_candidate_address: String,
180			pub d_parameter_policy_id: PolicyId,
181			pub permissioned_candidates_policy_id: PolicyId,
182		}
183
184		let MainChainScriptsEnvConfig {
185			committee_candidate_address,
186			d_parameter_policy_id,
187			permissioned_candidates_policy_id,
188		} = envy::from_env::<MainChainScriptsEnvConfig>()?;
189
190		let committee_candidate_address = FromStr::from_str(&committee_candidate_address)
191			.map_err(|err| envy::Error::Custom(format!("Incorrect main chain address: {}", err)))?;
192
193		Ok(Self {
194			committee_candidate_address,
195			d_parameter_policy_id,
196			permissioned_candidates_policy_id,
197		})
198	}
199}
200
201/// The part of data for selection of authorities that comes from the main chain.
202/// It is unfiltered, so the selection algorithm should filter out invalid candidates.
203#[derive(Clone, Debug, Encode, Decode, DecodeWithMemTracking, TypeInfo, PartialEq, Eq)]
204pub struct AuthoritySelectionInputs {
205	/// D-parameter for Ariadne committee selection. See [DParameter] for details.
206	pub d_parameter: DParameter,
207	/// List of permissioned candidates for committee selection.
208	pub permissioned_candidates: Vec<PermissionedCandidateData>,
209	/// List of registered candidates for committee selection
210	pub registered_candidates: Vec<CandidateRegistrations>,
211	/// Nonce for queried epoch.
212	pub epoch_nonce: EpochNonce,
213}
214
215sp_api::decl_runtime_apis! {
216	#[api_version(3)]
217	/// Runtime API declaration for Session Validator Management
218	pub trait SessionValidatorManagementApi<
219		AuthorityId,
220		AuthorityKeys,
221		ScEpochNumber: parity_scale_codec::Encode + parity_scale_codec::Decode
222	> where
223		AuthorityId: Encode + Decode,
224		AuthorityKeys: Encode + Decode,
225	{
226		/// Returns main chain scripts
227		fn get_main_chain_scripts() -> MainChainScripts;
228		/// Returns next unset [sidechain_domain::ScEpochNumber]
229		fn get_next_unset_epoch_number() -> ScEpochNumber;
230
231		#[changed_in(2)]
232		/// Returns current committee
233		fn get_current_committee() -> (ScEpochNumber, sp_std::vec::Vec<CommitteeMember<AuthorityId, AuthorityKeys>>);
234		/// Returns current committee
235		fn get_current_committee() -> (ScEpochNumber, sp_std::vec::Vec<CommitteeMember<AuthorityId, AuthorityKeys>>);
236
237		#[changed_in(2)]
238		/// Returns next committee
239		fn get_next_committee() -> Option<(ScEpochNumber, sp_std::vec::Vec<CommitteeMember<AuthorityId, AuthorityKeys>>)>;
240		/// Returns next committee
241		fn get_next_committee() -> Option<(ScEpochNumber, sp_std::vec::Vec<CommitteeMember<AuthorityId, AuthorityKeys>>)>;
242
243		#[changed_in(2)]
244		/// Calculates committee
245		fn calculate_committee(
246			authority_selection_inputs: AuthoritySelectionInputs,
247			sidechain_epoch: ScEpochNumber
248		) -> Option<sp_std::vec::Vec<(AuthorityId, AuthorityKeys)>>;
249
250		/// Calculates committee
251		fn calculate_committee(
252			authority_selection_inputs: AuthoritySelectionInputs,
253			sidechain_epoch: ScEpochNumber
254		) -> Option<sp_std::vec::Vec<CommitteeMember<AuthorityId, AuthorityKeys>>>;
255	}
256}