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