1use 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#[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#[derive(Eq, PartialEq, Hash, Clone, Debug, Serialize, Deserialize)]
52pub struct ValidatorState {
53 pub network_address: String,
55 pub votes: u64,
57 pub account_public_key: AccountPublicKey,
59}
60
61#[derive(Eq, PartialEq, Hash, Clone, Debug, Default, InputObject)]
63pub struct Committee {
64 pub validators: BTreeMap<ValidatorPublicKey, ValidatorState>,
66 total_votes: u64,
68 quorum_threshold: u64,
70 validity_threshold: u64,
72 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 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 pub fn policy_mut(&mut self) -> &mut ResourceControlPolicy {
309 &mut self.policy
310 }
311}