linera_execution/
committee.rs

1// Copyright (c) Facebook, Inc. and its affiliates.
2// Copyright (c) Zefchain Labs, Inc.
3// SPDX-License-Identifier: Apache-2.0
4
5use std::{borrow::Cow, collections::BTreeMap, str::FromStr};
6
7use allocative::Allocative;
8use linera_base::crypto::{AccountPublicKey, CryptoError, ValidatorPublicKey};
9use serde::{Deserialize, Serialize};
10
11use crate::policy::ResourceControlPolicy;
12
13/// The identity of a validator.
14#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Debug)]
15pub struct ValidatorName(pub ValidatorPublicKey);
16
17impl Serialize for ValidatorName {
18    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
19    where
20        S: serde::ser::Serializer,
21    {
22        if serializer.is_human_readable() {
23            serializer.serialize_str(&self.to_string())
24        } else {
25            serializer.serialize_newtype_struct("ValidatorName", &self.0)
26        }
27    }
28}
29
30impl<'de> Deserialize<'de> for ValidatorName {
31    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
32    where
33        D: serde::de::Deserializer<'de>,
34    {
35        if deserializer.is_human_readable() {
36            let s = String::deserialize(deserializer)?;
37            let value = Self::from_str(&s).map_err(serde::de::Error::custom)?;
38            Ok(value)
39        } else {
40            #[derive(Deserialize)]
41            #[serde(rename = "ValidatorName")]
42            struct ValidatorNameDerived(ValidatorPublicKey);
43
44            let value = ValidatorNameDerived::deserialize(deserializer)?;
45            Ok(Self(value.0))
46        }
47    }
48}
49
50/// Public state of a validator.
51#[derive(Eq, PartialEq, Hash, Clone, Debug, Serialize, Deserialize, Allocative)]
52pub struct ValidatorState {
53    /// The network address (in a string format understood by the networking layer).
54    pub network_address: String,
55    /// The voting power.
56    pub votes: u64,
57    /// The public key of the account associated with the validator.
58    pub account_public_key: AccountPublicKey,
59}
60
61/// A set of validators (identified by their public keys) and their voting rights.
62#[derive(Eq, PartialEq, Hash, Clone, Debug, Default, Allocative)]
63#[cfg_attr(with_graphql, derive(async_graphql::InputObject))]
64pub struct Committee {
65    /// The validators in the committee.
66    pub validators: BTreeMap<ValidatorPublicKey, ValidatorState>,
67    /// The sum of all voting rights.
68    total_votes: u64,
69    /// The threshold to form a quorum.
70    quorum_threshold: u64,
71    /// The threshold to prove the validity of a statement.
72    validity_threshold: u64,
73    /// The policy agreed on for this epoch.
74    policy: ResourceControlPolicy,
75}
76
77impl Serialize for Committee {
78    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
79    where
80        S: serde::ser::Serializer,
81    {
82        if serializer.is_human_readable() {
83            CommitteeFull::from(self).serialize(serializer)
84        } else {
85            CommitteeMinimal::from(self).serialize(serializer)
86        }
87    }
88}
89
90impl<'de> Deserialize<'de> for Committee {
91    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
92    where
93        D: serde::de::Deserializer<'de>,
94    {
95        if deserializer.is_human_readable() {
96            let committee_full = CommitteeFull::deserialize(deserializer)?;
97            Committee::try_from(committee_full).map_err(serde::de::Error::custom)
98        } else {
99            let committee_minimal = CommitteeMinimal::deserialize(deserializer)?;
100            Ok(Committee::from(committee_minimal))
101        }
102    }
103}
104
105#[derive(Serialize, Deserialize)]
106#[serde(rename = "Committee")]
107struct CommitteeFull<'a> {
108    validators: Cow<'a, BTreeMap<ValidatorPublicKey, ValidatorState>>,
109    total_votes: u64,
110    quorum_threshold: u64,
111    validity_threshold: u64,
112    policy: Cow<'a, ResourceControlPolicy>,
113}
114
115#[derive(Serialize, Deserialize)]
116#[serde(rename = "Committee")]
117struct CommitteeMinimal<'a> {
118    validators: Cow<'a, BTreeMap<ValidatorPublicKey, ValidatorState>>,
119    policy: Cow<'a, ResourceControlPolicy>,
120}
121
122impl TryFrom<CommitteeFull<'static>> for Committee {
123    type Error = String;
124
125    fn try_from(committee_full: CommitteeFull) -> Result<Committee, Self::Error> {
126        let CommitteeFull {
127            validators,
128            total_votes,
129            quorum_threshold,
130            validity_threshold,
131            policy,
132        } = committee_full;
133        let committee = Committee::new(validators.into_owned(), policy.into_owned());
134        if total_votes != committee.total_votes {
135            Err(format!(
136                "invalid committee: total_votes is {}; should be {}",
137                total_votes, committee.total_votes,
138            ))
139        } else if quorum_threshold != committee.quorum_threshold {
140            Err(format!(
141                "invalid committee: quorum_threshold is {}; should be {}",
142                quorum_threshold, committee.quorum_threshold,
143            ))
144        } else if validity_threshold != committee.validity_threshold {
145            Err(format!(
146                "invalid committee: validity_threshold is {}; should be {}",
147                validity_threshold, committee.validity_threshold,
148            ))
149        } else {
150            Ok(committee)
151        }
152    }
153}
154
155impl<'a> From<&'a Committee> for CommitteeFull<'a> {
156    fn from(committee: &'a Committee) -> CommitteeFull<'a> {
157        let Committee {
158            validators,
159            total_votes,
160            quorum_threshold,
161            validity_threshold,
162            policy,
163        } = committee;
164        CommitteeFull {
165            validators: Cow::Borrowed(validators),
166            total_votes: *total_votes,
167            quorum_threshold: *quorum_threshold,
168            validity_threshold: *validity_threshold,
169            policy: Cow::Borrowed(policy),
170        }
171    }
172}
173
174impl From<CommitteeMinimal<'static>> for Committee {
175    fn from(committee_min: CommitteeMinimal) -> Committee {
176        let CommitteeMinimal { validators, policy } = committee_min;
177        Committee::new(validators.into_owned(), policy.into_owned())
178    }
179}
180
181impl<'a> From<&'a Committee> for CommitteeMinimal<'a> {
182    fn from(committee: &'a Committee) -> CommitteeMinimal<'a> {
183        let Committee {
184            validators,
185            total_votes: _,
186            quorum_threshold: _,
187            validity_threshold: _,
188            policy,
189        } = committee;
190        CommitteeMinimal {
191            validators: Cow::Borrowed(validators),
192            policy: Cow::Borrowed(policy),
193        }
194    }
195}
196
197impl std::fmt::Display for ValidatorName {
198    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
199        self.0.fmt(f)
200    }
201}
202
203impl std::str::FromStr for ValidatorName {
204    type Err = CryptoError;
205
206    fn from_str(s: &str) -> Result<Self, Self::Err> {
207        Ok(ValidatorName(ValidatorPublicKey::from_str(s)?))
208    }
209}
210
211impl From<ValidatorPublicKey> for ValidatorName {
212    fn from(value: ValidatorPublicKey) -> Self {
213        Self(value)
214    }
215}
216
217impl Committee {
218    pub fn new(
219        validators: BTreeMap<ValidatorPublicKey, ValidatorState>,
220        policy: ResourceControlPolicy,
221    ) -> Self {
222        let total_votes = validators.values().fold(0, |sum, state| sum + state.votes);
223        // Let N = 3f + 1 + k such that 0 <= k <= 2. (Notably ⌊k / 3⌋ = 0 and ⌊(2 - k) / 3⌋ = 0.)
224        // The following thresholds verify:
225        // * ⌊2 N / 3⌋ + 1 = ⌊(6f + 2 + 2k) / 3⌋ + 1 = 2f + 1 + k + ⌊(2 - k) / 3⌋ = N - f
226        // * ⌊(N + 2) / 3⌋= ⌊(3f + 3 + k) / 3⌋ = f + 1 + ⌊k / 3⌋ = f + 1
227        let quorum_threshold = 2 * total_votes / 3 + 1;
228        let validity_threshold = total_votes.div_ceil(3);
229
230        Committee {
231            validators,
232            total_votes,
233            quorum_threshold,
234            validity_threshold,
235            policy,
236        }
237    }
238
239    #[cfg(with_testing)]
240    pub fn make_simple(keys: Vec<(ValidatorPublicKey, AccountPublicKey)>) -> Self {
241        let map = keys
242            .into_iter()
243            .map(|(validator_key, account_key)| {
244                (
245                    validator_key,
246                    ValidatorState {
247                        network_address: "Tcp:localhost:8080".to_string(),
248                        votes: 100,
249                        account_public_key: account_key,
250                    },
251                )
252            })
253            .collect();
254        Committee::new(map, ResourceControlPolicy::default())
255    }
256
257    pub fn weight(&self, author: &ValidatorPublicKey) -> u64 {
258        match self.validators.get(author) {
259            Some(state) => state.votes,
260            None => 0,
261        }
262    }
263
264    pub fn keys_and_weights(&self) -> impl Iterator<Item = (ValidatorPublicKey, u64)> + '_ {
265        self.validators
266            .iter()
267            .map(|(name, validator)| (*name, validator.votes))
268    }
269
270    pub fn account_keys_and_weights(&self) -> impl Iterator<Item = (AccountPublicKey, u64)> + '_ {
271        self.validators
272            .values()
273            .map(|validator| (validator.account_public_key, validator.votes))
274    }
275
276    pub fn network_address(&self, author: &ValidatorPublicKey) -> Option<&str> {
277        self.validators
278            .get(author)
279            .map(|state| state.network_address.as_ref())
280    }
281
282    pub fn quorum_threshold(&self) -> u64 {
283        self.quorum_threshold
284    }
285
286    pub fn validity_threshold(&self) -> u64 {
287        self.validity_threshold
288    }
289
290    pub fn validators(&self) -> &BTreeMap<ValidatorPublicKey, ValidatorState> {
291        &self.validators
292    }
293
294    pub fn validator_addresses(&self) -> impl Iterator<Item = (ValidatorPublicKey, &str)> {
295        self.validators
296            .iter()
297            .map(|(name, validator)| (*name, &*validator.network_address))
298    }
299
300    pub fn total_votes(&self) -> u64 {
301        self.total_votes
302    }
303
304    pub fn policy(&self) -> &ResourceControlPolicy {
305        &self.policy
306    }
307
308    /// Returns a mutable reference to this committee's [`ResourceControlPolicy`].
309    pub fn policy_mut(&mut self) -> &mut ResourceControlPolicy {
310        &mut self.policy
311    }
312}