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