1use 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#[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#[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#[derive(Eq, PartialEq, Hash, Clone, Debug, Serialize, Deserialize)]
95pub struct ValidatorState {
96 pub network_address: String,
98 pub votes: u64,
100 pub account_public_key: AccountPublicKey,
102}
103
104#[derive(Eq, PartialEq, Hash, Clone, Debug, Default, InputObject)]
106pub struct Committee {
107 pub validators: BTreeMap<ValidatorPublicKey, ValidatorState>,
109 total_votes: u64,
111 quorum_threshold: u64,
113 validity_threshold: u64,
115 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 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 pub fn policy_mut(&mut self) -> &mut ResourceControlPolicy {
386 &mut self.policy
387 }
388}