authority_selection_inherents/
select_authorities.rs

1//! Functionality related to selecting the validators from the valid candidates
2
3use crate::MaybeFromCandidateKeys;
4use crate::authority_selection_inputs::AuthoritySelectionInputs;
5use crate::filter_invalid_candidates::{
6	filter_invalid_permissioned_candidates, filter_trustless_candidates_registrations,
7};
8use log::{info, warn};
9use plutus::*;
10use sidechain_domain::{EpochNonce, ScEpochNumber, UtxoId};
11use sp_core::{Get, U256, ecdsa};
12use sp_runtime::BoundedVec;
13use sp_session_validator_management::CommitteeMember;
14
15/// Selects authorities using the Ariadne selection algorithm and data sourced from Partner Chains smart contracts on Cardano.
16/// Seed is constructed from the MC epoch nonce and the sidechain epoch.
17pub fn select_authorities<
18	TAccountId: Clone + Ord + From<ecdsa::Public>,
19	TAccountKeys: Clone + Ord + MaybeFromCandidateKeys,
20	MaxAuthorities: Get<u32>,
21>(
22	genesis_utxo: UtxoId,
23	input: AuthoritySelectionInputs,
24	sidechain_epoch: ScEpochNumber,
25) -> Option<BoundedVec<CommitteeMember<TAccountId, TAccountKeys>, MaxAuthorities>> {
26	Some(BoundedVec::truncate_from(select_candidates::<TAccountId, TAccountKeys>(
27		genesis_utxo,
28		input,
29		sidechain_epoch,
30	)?))
31}
32
33fn select_candidates<
34	TAccountId: Clone + Ord + From<ecdsa::Public>,
35	TAccountKeys: Clone + Ord + MaybeFromCandidateKeys,
36>(
37	genesis_utxo: UtxoId,
38	input: AuthoritySelectionInputs,
39	sidechain_epoch: ScEpochNumber,
40) -> Option<Vec<CommitteeMember<TAccountId, TAccountKeys>>> {
41	let valid_registered_candidates = filter_trustless_candidates_registrations::<
42		TAccountId,
43		TAccountKeys,
44	>(input.registered_candidates, genesis_utxo);
45	let valid_permissioned_candidates = filter_invalid_permissioned_candidates::<
46		TAccountId,
47		TAccountKeys,
48	>(input.permissioned_candidates);
49	let valid_permissioned_count = valid_permissioned_candidates.len();
50	let valid_registered_count = valid_registered_candidates.len();
51
52	let random_seed = seed_from_nonce_and_sc_epoch(&input.epoch_nonce, &sidechain_epoch);
53
54	if let Some(validators) = selection::ariadne_v2::select_authorities(
55		input.d_parameter.num_registered_candidates,
56		input.d_parameter.num_permissioned_candidates,
57		valid_registered_candidates,
58		valid_permissioned_candidates,
59		random_seed,
60	) {
61		info!(
62			"💼 Selected committee of {} seats for epoch {} from {valid_permissioned_count} permissioned and {valid_registered_count} registered candidates",
63			validators.len(),
64			sidechain_epoch
65		);
66		Some(validators.into_iter().map(|member| member.into()).collect())
67	} else {
68		warn!("🚫 Failed to select validators for epoch {}", sidechain_epoch);
69		None
70	}
71}
72
73/// Generate 32 byte seed from epoch nonce and Partner Chain epoch number
74pub fn seed_from_nonce_and_sc_epoch(
75	epoch_nonce: &EpochNonce,
76	partner_chain_epoch_number: &ScEpochNumber,
77) -> [u8; 32] {
78	U256::from_big_endian(&epoch_nonce.as_array())
79		.overflowing_add(U256::from(partner_chain_epoch_number.0))
80		.0
81		.to_big_endian()
82}
83
84#[cfg(test)]
85mod tests {
86	use super::*;
87	use sidechain_domain::{EpochNonce, ScEpochNumber};
88	use sp_core::U256;
89
90	#[test]
91	fn should_create_correct_seed() {
92		let nonce_vec = Vec::from(U256::from(10).to_big_endian());
93		assert_eq!(
94			seed_from_nonce_and_sc_epoch(&EpochNonce(nonce_vec), &ScEpochNumber(2)),
95			U256::from(12).to_big_endian()
96		);
97	}
98}