frame_suite/elections.rs
1// SPDX-License-Identifier: MPL-2.0
2//
3// Part of Auguth Labs open-source softwares.
4// Built for the Substrate framework.
5//
6// This Source Code Form is subject to the terms of the Mozilla Public
7// License, v. 2.0. If a copy of the MPL was not distributed with this
8// file, You can obtain one at https://mozilla.org/MPL/2.0/.
9//
10// Copyright (c) 2026 Auguth Labs (OPC) Pvt Ltd, India
11
12// ===============================================================================
13// ``````````````````````````````` ELECTIONS SUITE ```````````````````````````````
14// ===============================================================================
15
16//! Provides generic traits for **election management**, **weight computation**,
17//! and **influence calculation** using [`plugin`](crate::plugins)-based models.
18//!
19//! The traits include:
20//! - [`ElectionManager`]: Core election management for candidates.
21//! - [`InspectWeight`]: Provides candidate weight lookup.
22//! - [`Influence`]: Computes influence values from raw input using plugin models.
23//!
24//! ## Note
25//!
26//! Elections are **pluggable by design**, meaning:
27//! - The **inputs** generally consist of candidates along with their backing
28//! weights (or votes).
29//! - The **outputs** can be a single candidate or a collection of candidates
30//! (stored via an iterator).
31//!
32//! Because of this, we define **generic plugin-based traits**, enabling:
33//! 1. Multiple election models to be implemented over the same trait interfaces.
34//! 2. Flexible storage and computation strategies depending on the election logic.
35//! 3. Strong type-safety while maintaining runtime configurability via plugins.
36
37// ===============================================================================
38// ``````````````````````````````````` IMPORTS ```````````````````````````````````
39// ===============================================================================
40
41// --- Local crate imports ---
42use crate::{
43 base::{Buffer, Delimited, Elastic, Keyed, RuntimeType, Sortable, Storable},
44 plugin_output, plugin_types,
45};
46
47// --- Substrate primitives ---
48use sp_runtime::{DispatchError, DispatchResult};
49
50// ===============================================================================
51// `````````````````````````````` ELECTION MANAGER ```````````````````````````````
52// ===============================================================================
53
54/// A trait for **managing elections** of candidates with associated weights.
55///
56/// This trait is designed to be **highly generic** and
57/// [`plugin-driven`](crate::plugins), enabling multiple election models
58/// to be used without modifying the trait itself.
59///
60/// Elections are inherently **pluggable and diverse**:
61/// - Different elections may use distinct **weighting rules** or
62/// **selection algorithms**.
63/// - Inputs consist of candidates paired with their backing weights.
64/// - Outputs may represent either a single winner or multiple winners.
65/// - A plugin-based design allows flexible implementations while
66/// preserving type safety and runtime configurability.
67///
68/// ## Type Parameters
69/// - `Candidate`: The type representing a candidate in the election.
70pub trait ElectionManager<Candidate>: InspectWeight<Candidate, Self::ElectionWeightOf>
71where
72 Candidate: Keyed,
73{
74 /// Represents a single vote or its associated weight.
75 ///
76 /// This can either:
77 /// - Store a numeric weight for a vote, or
78 /// - Represent a single vote implicitly.
79 ///
80 /// Must implement [`Ord`] to allow comparison and sorting within
81 /// [`Self::ElectionWeightOf`].
82 type ElectionWeight: Sortable;
83
84 /// Collection type for storing candidate weights.
85 ///
86 /// This allows flexibility in the underlying container, such as:
87 /// - `Vec`
88 /// - Arrays
89 /// - Custom buffer types
90 ///
91 /// Must support:
92 /// - Iteration via [`Buffer`]
93 /// - Ordering via [`Ord`]
94 /// - Storage via [`Storable`]
95 type ElectionWeightOf: Buffer<Self::ElectionWeight> + Ord + Storable;
96
97 /// Input type for election computation.
98 ///
99 /// Each entry maps a candidate to a collection of associated weights.
100 ///
101 /// This abstraction ensures that plugin implementations only accept
102 /// well-structured and compatible input formats.
103 type Params: Buffer<(Candidate, Self::ElectionWeightOf)>;
104
105 /// Output type representing elected candidates.
106 ///
107 /// This can represent:
108 /// - Multiple winners (e.g., committee selection), or
109 /// - A single winner (as a single-element collection)
110 ///
111 /// If the output implies ranking, the elements are expected to be
112 /// ordered by priority.
113 ///
114 /// If truncation occurs (e.g., selecting top-N winners), the ordering
115 /// must reflect the final priority of elected candidates.
116 type Elected: Buffer<Candidate>;
117
118 plugin_types!(
119 input: Self::Params,
120 output: Self::Elected,
121 /// The plugin responsible for computing election results.
122 ///
123 /// This model defines the election strategy by:
124 /// - Consuming [`Self::Params`] as input, and
125 /// - Producing [`Self::Elected`] as output.
126 ///
127 /// This abstraction allows multiple election strategies to be
128 /// plugged in safely and interchangeably.
129 ///
130 /// If the resulting [`Self::Elected`] is truncated (e.g., selecting top-N),
131 /// the candidates must be ordered by priority.
132 model: ElectionModel,
133
134 /// Provides runtime configuration for the [`Self::ElectionModel`] computation.
135 ///
136 /// This context supplies dynamic parameters such as:
137 /// - Thresholds
138 /// - Weights
139 /// - Other runtime-specific settings
140 ///
141 /// It enables flexible behavior without requiring hardcoded values.
142 context: ElectionContext,
143 );
144
145 /// Executes the election process and persists the results.
146 ///
147 /// This function:
148 /// 1. Runs the election model using [`Self::run_model`]
149 /// 2. Stores the resulting elected candidates via [`Self::store`]
150 fn prepare(from: Self::Params) -> DispatchResult {
151 // Computes the plugin output
152 let out = &Self::run_model(from);
153 // stores the output of elected candidates
154 if let Err(e) = Self::store(out) {
155 Self::on_prepare_fail(e);
156 return Ok(());
157 };
158 Self::on_prepare_success(out);
159 Ok(())
160 }
161
162 plugin_output! {
163 /// [`Self::ElectionModel`] plugin output function.
164 ///
165 /// Utilizes the plugin model's context [`Self::ElectionContext`]
166 fn run_model,
167 input: Self::Params,
168 output: Self::Elected,
169 model: Self::ElectionModel,
170 context: Self::ElectionContext
171 }
172
173 /// Persist the election results. Must be implemented by the consumer.
174 fn store(_elects: &Self::Elected) -> DispatchResult;
175
176 /// Retrieve currently elected candidates.
177 fn reveal() -> Option<Self::Elected>;
178
179 /// Remove a candidate from the elected pool.
180 fn remove(who: &Candidate);
181
182 /// Check if a candidate exist in the elected pool.
183 fn is_candidate(who: &Candidate) -> DispatchResult;
184
185 /// Check if election preparation is possible with the given parameters.
186 fn can_prepare(from: &Self::Params) -> DispatchResult;
187
188 /// Hook called after a successful election preparation. Default is no-op.
189 fn on_prepare_success(_elects: &Self::Elected) {}
190
191 /// Hook called after an election preparation failure. Default is no-op.
192 fn on_prepare_fail(_err: DispatchError) {}
193}
194
195// ===============================================================================
196// ``````````````````````````````` INSPECT WEIGHT ````````````````````````````````
197// ===============================================================================
198
199/// Trait for inspecting the **weight of a candidate** for an upcoming
200/// election.
201///
202/// - Different election models may compute or store weights differently.
203/// - Providing a trait allows generic election managers or plugins
204/// to query a candidate's weight without knowing the underlying structure.
205///
206/// ## Type Parameters
207/// - `Candidate`: The type representing a candidate.
208/// - `Weight`: The type representing the candidate's vote weight.
209pub trait InspectWeight<Candidate, Weight>
210where
211 Candidate: Keyed,
212 Weight: RuntimeType,
213{
214 /// Return the weight of a candidate if available.
215 ///
216 /// Returns an error if the candidate has no associated weight.
217 fn weight_of(who: &Candidate) -> Result<Weight, DispatchError>;
218}
219
220// ===============================================================================
221// `````````````````````````````````` INFLUENCE ``````````````````````````````````
222// ===============================================================================
223
224/// A trait for computing **influence**, a normalized and comparable metric
225/// representing the relative power or importance of an entity.
226///
227/// Influence is intended to capture **non-transferable system weight**,
228/// derived from various inputs such as votes, stake, participation,
229/// or other domain-specific factors.
230///
231/// ## Key Properties
232///
233/// - **Non-transferable**:
234/// Influence is a derived metric and must not be directly traded or transferred.
235/// - **Model-dependent**:
236/// Different systems may define influence differently based on their
237/// weighting rules or algorithms.
238/// - **Deterministic**:
239/// Given the same input and context, the computed influence should be consistent.
240/// - **Comparable**:
241/// Influence values must be bounded and comparable across entities.
242///
243/// ## Design
244///
245/// This trait follows a [`plugin`](crate::plugins)-based architecture:
246/// - The computation logic is delegated to pluggable models.
247/// - Different influence strategies can coexist without changing the trait.
248/// - Runtime configuration is supported via context.
249///
250/// ## Type Parameters
251/// - `RawFrom`: The raw input type used to derive influence.
252///
253/// This allows flexibility in supporting various input formats, such as:
254/// - Numeric values (e.g., stake or balance)
255/// - Account identifiers
256/// - Structured or aggregated data
257pub trait Influence<RawFrom>
258where
259 RawFrom: Elastic,
260{
261 /// Type representing the computed influence.
262 type Influence: Delimited;
263
264 plugin_types!(
265 input: RawFrom,
266 output: Self::Influence,
267 /// The plugin responsible for computing influence.
268 ///
269 /// This model defines how raw input is transformed into
270 /// a bounded influence value.
271 ///
272 /// Different models may implement:
273 /// - Linear scaling
274 /// - Weighted aggregation
275 /// - Non-linear transformations (e.g., logarithmic influence)
276 model: InfluenceModel,
277
278 /// Provides runtime configuration for [`Self::InfluenceModel`].
279 ///
280 /// This context supplies dynamic parameters such as:
281 /// - Scaling factors
282 /// - Thresholds
283 /// - External weights or modifiers
284 ///
285 /// This enables flexible and adaptive influence computation
286 /// without hardcoding logic.
287 context: InfluenceContext,
288 );
289
290 plugin_output! {
291 /// Computes influence from a raw input using the configured plugin.
292 ///
293 /// This function:
294 /// - Delegates computation to [`Self::InfluenceModel`]
295 /// - Uses [`Self::InfluenceContext`] for parameterization
296 ///
297 /// ## Returns
298 /// A bounded influence value derived from the given input.
299 fn influence,
300 input: RawFrom,
301 output: Self::Influence,
302 model: Self::InfluenceModel,
303 context: Self::InfluenceContext
304 }
305}