authority_selection_inherents/
select_authorities.rs

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