starfish_config/
committee.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use std::{
6    fmt::{Display, Formatter},
7    ops::{Index, IndexMut},
8};
9
10use iota_network_stack::Multiaddr;
11use serde::{Deserialize, Serialize};
12
13use crate::{AuthorityPublicKey, NetworkPublicKey, ProtocolPublicKey};
14
15/// Committee of the consensus protocol is updated each epoch.
16pub type Epoch = u64;
17
18/// Voting power of an authority, roughly proportional to the actual amount of
19/// IOTA staked by the authority.
20/// Total stake / voting power of all authorities should sum to 10,000.
21pub type Stake = u64;
22
23/// Committee is the set of authorities that participate in the consensus
24/// protocol for this epoch. Its configuration is stored and computed on chain.
25/// Committee size is currently limited to 256 as AuthorityIndex is u8.
26#[derive(Clone, Debug, Serialize, Deserialize)]
27pub struct Committee {
28    /// The epoch number of this committee
29    epoch: Epoch,
30    /// Total stake in the committee.
31    total_stake: Stake,
32    /// The quorum threshold (2f+1).
33    quorum_threshold: Stake,
34    /// The validity threshold (f+1).
35    validity_threshold: Stake,
36    /// Protocol and network info of each authority. Max number limited to
37    /// u8::MAX (256)
38    authorities: Vec<Authority>,
39    /// transaction data in a block is divided into info_length equal
40    /// parts(shards) that are encoded into n shards with erasure correcting
41    /// code info_length equals n-2f for the case with uniform stakes
42    info_length: usize,
43}
44
45impl Committee {
46    pub fn new(epoch: Epoch, authorities: Vec<Authority>) -> Self {
47        assert!(!authorities.is_empty(), "Committee cannot be empty!");
48        assert!(
49            authorities.len() < u8::MAX as usize,
50            "Too many authorities ({})!",
51            authorities.len()
52        );
53
54        let total_stake = authorities.iter().map(|a| a.stake).sum::<u64>();
55        assert_ne!(total_stake, 0, "Total stake cannot be zero!");
56        let quorum_threshold = 2 * total_stake / 3 + 1;
57        let validity_threshold = total_stake.div_ceil(3);
58        let committee_size = authorities.len();
59        // f and info_length are computed for uniform stakes
60        // TODO: change when we implement encoding/decoding for non-uniform stakes
61        let f = (committee_size - 1) / 3;
62        let info_length = committee_size - 2 * f;
63        Self {
64            epoch,
65            total_stake,
66            quorum_threshold,
67            validity_threshold,
68            authorities,
69            info_length,
70        }
71    }
72
73    // -----------------------------------------------------------------------
74    // Accessors to Committee fields.
75
76    pub fn epoch(&self) -> Epoch {
77        self.epoch
78    }
79
80    pub fn total_stake(&self) -> Stake {
81        self.total_stake
82    }
83
84    pub fn quorum_threshold(&self) -> Stake {
85        self.quorum_threshold
86    }
87
88    pub fn validity_threshold(&self) -> Stake {
89        self.validity_threshold
90    }
91
92    pub fn info_length(&self) -> usize {
93        self.info_length
94    }
95
96    pub fn parity_length(&self) -> usize {
97        self.size() - self.info_length()
98    }
99
100    pub fn stake(&self, authority_index: AuthorityIndex) -> Stake {
101        self.authorities[authority_index].stake
102    }
103
104    pub fn authority(&self, authority_index: AuthorityIndex) -> &Authority {
105        &self.authorities[authority_index]
106    }
107
108    pub fn authorities(&self) -> impl Iterator<Item = (AuthorityIndex, &Authority)> {
109        self.authorities
110            .iter()
111            .enumerate()
112            .map(|(i, a)| (AuthorityIndex(i as u8), a))
113    }
114
115    // -----------------------------------------------------------------------
116    // Helpers for Committee properties.
117
118    /// Returns true if the provided stake has reached quorum (2f+1).
119    pub fn reached_quorum(&self, stake: Stake) -> bool {
120        stake >= self.quorum_threshold()
121    }
122
123    /// Returns true if the provided stake has reached validity (f+1).
124    pub fn reached_validity(&self, stake: Stake) -> bool {
125        stake >= self.validity_threshold()
126    }
127
128    /// Coverts an index to an AuthorityIndex, if valid.
129    /// Returns None if index is out of bound.
130    pub fn to_authority_index(&self, index: usize) -> Option<AuthorityIndex> {
131        if index < self.authorities.len() {
132            Some(AuthorityIndex(index as u8))
133        } else {
134            None
135        }
136    }
137
138    /// Returns true if the provided index is valid.
139    pub fn is_valid_index(&self, index: AuthorityIndex) -> bool {
140        index.value() < self.size()
141    }
142
143    /// Returns number of authorities in the committee.
144    pub fn size(&self) -> usize {
145        self.authorities.len()
146    }
147}
148
149/// Represents one authority in the committee.
150///
151/// NOTE: this is intentionally un-cloneable, to encourage only copying relevant
152/// fields. AuthorityIndex should be used to reference an authority instead.
153#[derive(Clone, Debug, Serialize, Deserialize)]
154pub struct Authority {
155    /// Voting power of the authority in the committee.
156    pub stake: Stake,
157    /// Network address for communicating with the authority.
158    pub address: Multiaddr,
159    /// The authority's hostname, for metrics and logging.
160    pub hostname: String,
161    /// The public key bytes corresponding to the private key that the validator
162    /// holds to sign transactions.
163    pub authority_key: AuthorityPublicKey,
164    /// The public key bytes corresponding to the private key that the validator
165    /// holds to sign consensus blocks.
166    pub protocol_key: ProtocolPublicKey,
167    /// The public key bytes corresponding to the private key that the validator
168    /// uses to establish TLS connections.
169    pub network_key: NetworkPublicKey,
170}
171
172/// Each authority is uniquely identified by its AuthorityIndex in the
173/// Committee. AuthorityIndex is between 0 (inclusive) and the total number of
174/// authorities (exclusive) limited by `u8` to 255.
175///
176/// NOTE: for safety, invalid AuthorityIndex should be impossible to create. So
177/// AuthorityIndex should not be created or incremented outside of this file.
178/// AuthorityIndex received from peers should be validated before use.
179#[derive(
180    Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Debug, Default, Hash, Serialize, Deserialize,
181)]
182pub struct AuthorityIndex(u8);
183
184impl AuthorityIndex {
185    // Minimum committee size is 1, so 0 index is always valid.
186    pub const ZERO: Self = Self(0);
187
188    // Only for scanning rows in the database. Invalid elsewhere.
189    pub const MIN: Self = Self::ZERO;
190    pub const MAX: Self = Self(u8::MAX);
191
192    pub fn value(&self) -> usize {
193        self.0 as usize
194    }
195}
196
197impl From<u8> for AuthorityIndex {
198    fn from(value: u8) -> Self {
199        Self(value)
200    }
201}
202
203impl AuthorityIndex {
204    pub fn new_for_test(index: u8) -> Self {
205        Self(index)
206    }
207}
208
209impl Display for AuthorityIndex {
210    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
211        write!(f, "[{}]", self.value())
212    }
213}
214
215impl<T, const N: usize> Index<AuthorityIndex> for [T; N] {
216    type Output = T;
217
218    fn index(&self, index: AuthorityIndex) -> &Self::Output {
219        self.get(index.value()).unwrap()
220    }
221}
222
223impl<T> Index<AuthorityIndex> for Vec<T> {
224    type Output = T;
225
226    fn index(&self, index: AuthorityIndex) -> &Self::Output {
227        self.get(index.value()).unwrap()
228    }
229}
230
231impl<T, const N: usize> IndexMut<AuthorityIndex> for [T; N] {
232    fn index_mut(&mut self, index: AuthorityIndex) -> &mut Self::Output {
233        self.get_mut(index.value()).unwrap()
234    }
235}
236
237impl<T> IndexMut<AuthorityIndex> for Vec<T> {
238    fn index_mut(&mut self, index: AuthorityIndex) -> &mut Self::Output {
239        self.get_mut(index.value()).unwrap()
240    }
241}
242
243#[cfg(test)]
244mod tests {
245    use super::*;
246    use crate::local_committee_and_keys;
247
248    #[test]
249    fn committee_basic() {
250        // GIVEN
251        let epoch = 100;
252        let num_of_authorities = 9;
253        let authority_stakes = (1..=9).map(|s| s as Stake).collect();
254        let (committee, _) = local_committee_and_keys(epoch, authority_stakes);
255
256        // THEN make sure the output Committee fields are populated correctly.
257        assert_eq!(committee.size(), num_of_authorities);
258        for (i, authority) in committee.authorities() {
259            assert_eq!((i.value() + 1) as Stake, authority.stake);
260        }
261
262        // AND ensure thresholds are calculated correctly.
263        assert_eq!(committee.total_stake(), 45);
264        assert_eq!(committee.quorum_threshold(), 31);
265        assert_eq!(committee.validity_threshold(), 15);
266    }
267}