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. I.e. the assumption is that strictly
72    /// less than `validity_threshold` are faulty.
73    validity_threshold: u64,
74    /// The policy agreed on for this epoch.
75    policy: ResourceControlPolicy,
76}
77
78impl Serialize for Committee {
79    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
80    where
81        S: serde::ser::Serializer,
82    {
83        if serializer.is_human_readable() {
84            CommitteeFull::from(self).serialize(serializer)
85        } else {
86            CommitteeMinimal::from(self).serialize(serializer)
87        }
88    }
89}
90
91impl<'de> Deserialize<'de> for Committee {
92    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
93    where
94        D: serde::de::Deserializer<'de>,
95    {
96        if deserializer.is_human_readable() {
97            let committee_full = CommitteeFull::deserialize(deserializer)?;
98            Committee::try_from(committee_full).map_err(serde::de::Error::custom)
99        } else {
100            let committee_minimal = CommitteeMinimal::deserialize(deserializer)?;
101            Ok(Committee::from(committee_minimal))
102        }
103    }
104}
105
106#[derive(Serialize, Deserialize)]
107#[serde(rename = "Committee")]
108struct CommitteeFull<'a> {
109    validators: Cow<'a, BTreeMap<ValidatorPublicKey, ValidatorState>>,
110    total_votes: u64,
111    quorum_threshold: u64,
112    validity_threshold: u64,
113    policy: Cow<'a, ResourceControlPolicy>,
114}
115
116#[derive(Serialize, Deserialize)]
117#[serde(rename = "Committee")]
118struct CommitteeMinimal<'a> {
119    validators: Cow<'a, BTreeMap<ValidatorPublicKey, ValidatorState>>,
120    policy: Cow<'a, ResourceControlPolicy>,
121}
122
123impl TryFrom<CommitteeFull<'static>> for Committee {
124    type Error = String;
125
126    fn try_from(committee_full: CommitteeFull) -> Result<Committee, Self::Error> {
127        let CommitteeFull {
128            validators,
129            total_votes,
130            quorum_threshold,
131            validity_threshold,
132            policy,
133        } = committee_full;
134        let committee = Committee::new(validators.into_owned(), policy.into_owned());
135        if total_votes != committee.total_votes {
136            Err(format!(
137                "invalid committee: total_votes is {}; should be {}",
138                total_votes, committee.total_votes,
139            ))
140        } else if quorum_threshold != committee.quorum_threshold {
141            Err(format!(
142                "invalid committee: quorum_threshold is {}; should be {}",
143                quorum_threshold, committee.quorum_threshold,
144            ))
145        } else if validity_threshold != committee.validity_threshold {
146            Err(format!(
147                "invalid committee: validity_threshold is {}; should be {}",
148                validity_threshold, committee.validity_threshold,
149            ))
150        } else {
151            Ok(committee)
152        }
153    }
154}
155
156impl<'a> From<&'a Committee> for CommitteeFull<'a> {
157    fn from(committee: &'a Committee) -> CommitteeFull<'a> {
158        let Committee {
159            validators,
160            total_votes,
161            quorum_threshold,
162            validity_threshold,
163            policy,
164        } = committee;
165        CommitteeFull {
166            validators: Cow::Borrowed(validators),
167            total_votes: *total_votes,
168            quorum_threshold: *quorum_threshold,
169            validity_threshold: *validity_threshold,
170            policy: Cow::Borrowed(policy),
171        }
172    }
173}
174
175impl From<CommitteeMinimal<'static>> for Committee {
176    fn from(committee_min: CommitteeMinimal) -> Committee {
177        let CommitteeMinimal { validators, policy } = committee_min;
178        Committee::new(validators.into_owned(), policy.into_owned())
179    }
180}
181
182impl<'a> From<&'a Committee> for CommitteeMinimal<'a> {
183    fn from(committee: &'a Committee) -> CommitteeMinimal<'a> {
184        let Committee {
185            validators,
186            total_votes: _,
187            quorum_threshold: _,
188            validity_threshold: _,
189            policy,
190        } = committee;
191        CommitteeMinimal {
192            validators: Cow::Borrowed(validators),
193            policy: Cow::Borrowed(policy),
194        }
195    }
196}
197
198impl std::fmt::Display for ValidatorName {
199    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
200        self.0.fmt(f)
201    }
202}
203
204impl std::str::FromStr for ValidatorName {
205    type Err = CryptoError;
206
207    fn from_str(s: &str) -> Result<Self, Self::Err> {
208        Ok(ValidatorName(ValidatorPublicKey::from_str(s)?))
209    }
210}
211
212impl From<ValidatorPublicKey> for ValidatorName {
213    fn from(value: ValidatorPublicKey) -> Self {
214        Self(value)
215    }
216}
217
218impl Committee {
219    pub fn new(
220        validators: BTreeMap<ValidatorPublicKey, ValidatorState>,
221        policy: ResourceControlPolicy,
222    ) -> Self {
223        let total_votes = validators.values().fold(0, |sum, state| sum + state.votes);
224        // The validity threshold is f + 1, where f is maximal so that it is less than a third.
225        // So the threshold is N / 3, rounded up.
226        let validity_threshold = total_votes.div_ceil(3);
227        // The quorum threshold is minimal such that any two quorums intersect in at least one
228        // validity threshold.
229        let quorum_threshold = (total_votes + validity_threshold).div_ceil(2);
230
231        Committee {
232            validators,
233            total_votes,
234            quorum_threshold,
235            validity_threshold,
236            policy,
237        }
238    }
239
240    #[cfg(with_testing)]
241    pub fn make_simple(keys: Vec<(ValidatorPublicKey, AccountPublicKey)>) -> Self {
242        let map = keys
243            .into_iter()
244            .map(|(validator_key, account_key)| {
245                (
246                    validator_key,
247                    ValidatorState {
248                        network_address: "Tcp:localhost:8080".to_string(),
249                        votes: 100,
250                        account_public_key: account_key,
251                    },
252                )
253            })
254            .collect();
255        Committee::new(map, ResourceControlPolicy::default())
256    }
257
258    pub fn weight(&self, author: &ValidatorPublicKey) -> u64 {
259        match self.validators.get(author) {
260            Some(state) => state.votes,
261            None => 0,
262        }
263    }
264
265    pub fn keys_and_weights(&self) -> impl Iterator<Item = (ValidatorPublicKey, u64)> + '_ {
266        self.validators
267            .iter()
268            .map(|(name, validator)| (*name, validator.votes))
269    }
270
271    pub fn account_keys_and_weights(&self) -> impl Iterator<Item = (AccountPublicKey, u64)> + '_ {
272        self.validators
273            .values()
274            .map(|validator| (validator.account_public_key, validator.votes))
275    }
276
277    pub fn network_address(&self, author: &ValidatorPublicKey) -> Option<&str> {
278        self.validators
279            .get(author)
280            .map(|state| state.network_address.as_ref())
281    }
282
283    pub fn quorum_threshold(&self) -> u64 {
284        self.quorum_threshold
285    }
286
287    pub fn validity_threshold(&self) -> u64 {
288        self.validity_threshold
289    }
290
291    pub fn validators(&self) -> &BTreeMap<ValidatorPublicKey, ValidatorState> {
292        &self.validators
293    }
294
295    pub fn validator_addresses(&self) -> impl Iterator<Item = (ValidatorPublicKey, &str)> {
296        self.validators
297            .iter()
298            .map(|(name, validator)| (*name, &*validator.network_address))
299    }
300
301    pub fn total_votes(&self) -> u64 {
302        self.total_votes
303    }
304
305    pub fn policy(&self) -> &ResourceControlPolicy {
306        &self.policy
307    }
308
309    /// Returns a mutable reference to this committee's [`ResourceControlPolicy`].
310    pub fn policy_mut(&mut self) -> &mut ResourceControlPolicy {
311        &mut self.policy
312    }
313}