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